package grpc_errcode

import (
	"context"

	"github.com/tidbcloud/account/central/utils"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/pingcap/errcode"
	grpcerr "github.com/pingcap/errcode/grpc"
	"github.com/tidbcloud/tiinf/errs"
	"google.golang.org/grpc"
)

type ErrorFormat int

const (
	ErrorFormatDefault ErrorFormat = iota
	ErrorFormatOpenAPI
	ErrorFormatCentral
)

// errorConvert converts an errcode error to a GRPC error
func errorConvert(ctx context.Context, err error, errorFormat ErrorFormat) error {
	if err == nil {
		return nil
	}

	if coded := errcode.CodeChain(err); coded != nil {
		err = grpcerr.WrapAsGRPC(coded)
	}

	// Return raw errors by default, otherwise return OpenAPI format errors
	if errorFormat == ErrorFormatOpenAPI || errorFormat == ErrorFormatCentral {
		if errs.IsError(err, errs.TiErr) {
			if _, ok := errs.AsTiError(err); ok {
				err = utils.ConvertErr2GrpcErr(err)
			}
		}
	}

	return err
}

func UnaryServerInterceptor(errorFormat ErrorFormat) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		resp, err := handler(ctx, req)
		return resp, errorConvert(ctx, err, errorFormat)
	}
}

func StreamServerInterceptor(errorFormat ErrorFormat) grpc.StreamServerInterceptor {
	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		wrapped := grpc_middleware.WrapServerStream(stream)
		err := handler(srv, wrapped)
		return errorConvert(stream.Context(), err, errorFormat)
	}
}
