package utils

import (
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/serverless-service/internal/errorcode"
	"google.golang.org/grpc/codes"
	"google.golang.org/protobuf/types/known/structpb"
)

var (
	gprcCode2BizCodeMapping = map[codes.Code]bizerr.ErrCode{
		codes.Internal:          bizerr.ErrCodeUnknown,
		codes.OK:                bizerr.ErrCodeOK,
		codes.InvalidArgument:   bizerr.ErrCodeParams,
		codes.DeadlineExceeded:  bizerr.ErrCodeTimeout,
		codes.NotFound:          bizerr.ErrCodeRecordNotFound,
		codes.AlreadyExists:     bizerr.ErrCodeRecordExist,
		codes.PermissionDenied:  bizerr.ErrCodePermission,
		codes.ResourceExhausted: bizerr.ErrCodeTooManyRequests,
		codes.Unauthenticated:   bizerr.ErrCodeAuth,
	}

	bizCode2GrpcCodeMapping = map[bizerr.ErrCode]codes.Code{
		bizerr.ErrCodeUnknown:         codes.Internal,
		bizerr.ErrCodeOK:              codes.OK,
		bizerr.ErrCodeParams:          codes.InvalidArgument,
		bizerr.ErrCodeTimeout:         codes.DeadlineExceeded,
		bizerr.ErrCodeRecordNotFound:  codes.NotFound,
		bizerr.ErrCodeRecordExist:     codes.AlreadyExists,
		bizerr.ErrCodePermission:      codes.PermissionDenied,
		bizerr.ErrCodeTooManyRequests: codes.ResourceExhausted,
		bizerr.ErrCodeAuth:            codes.Unauthenticated,
		// 409
		errorcode.ErrCodeBranchNotRunning:                 codes.Aborted,
		errorcode.ErrCodeClusterNotRunning:                codes.Aborted,
		errorcode.ErrCodeCreateBranchFromThrottledCluster: codes.Aborted,
	}
)

const (
	MinCustomizedBizErrCode = 40000000
	MaxGrpcErrCode          = bizerr.ErrCode(codes.Unauthenticated)
)

func GrpcCode2BizCode(code codes.Code) bizerr.ErrCode {
	if code >= MinCustomizedBizErrCode {
		return bizerr.ErrCode(code)
	}

	if bizErrCode, ok := gprcCode2BizCodeMapping[code]; ok {
		return bizErrCode
	}
	return bizerr.ErrCodeUnknown
}

func BizCode2GrpcCode(bizcode bizerr.ErrCode) codes.Code {
	if bizcode <= MaxGrpcErrCode {
		return codes.Code(bizcode)
	}

	if grpcCode, ok := bizCode2GrpcCodeMapping[bizcode]; ok {
		return grpcCode
	}

	if bizerr.IsBusinessErr(bizcode) {
		return codes.InvalidArgument
	}

	return codes.Internal
}

func ListMap2StructpbValue(m map[string][]any) (*structpb.Value, error) {
	val := &structpb.Struct{Fields: make(map[string]*structpb.Value, len(m))}
	for k, v := range m {
		x, err := structpb.NewList(v)
		if err != nil {
			return nil, err
		}
		val.Fields[k] = structpb.NewListValue(x)
	}
	return structpb.NewStructValue(val), nil
}

func StringSlice2StructpbValue(s []string) *structpb.Value {
	val := &structpb.ListValue{Values: make([]*structpb.Value, len(s))}
	for i, v := range s {
		val.Values[i] = structpb.NewStringValue(v)
	}
	return structpb.NewListValue(val)
}
