package interceptor

import (
	"context"
	"net/http"

	"github.com/pingcap/errcode"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var httpCode2BizErrorCodeMapping = map[int]bizerr.ErrCode{
	http.StatusInternalServerError: bizerr.ErrCodeUnknown,
	http.StatusOK:                  bizerr.ErrCodeOK,
	http.StatusBadRequest:          bizerr.ErrCodeParams,
	http.StatusGatewayTimeout:      bizerr.ErrCodeTimeout,
	http.StatusNotFound:            bizerr.ErrCodeRecordNotFound,
	http.StatusConflict:            bizerr.ErrCodeRecordExist,
	http.StatusForbidden:           bizerr.ErrCodePermission,
	http.StatusTooManyRequests:     bizerr.ErrCodeTooManyRequests,
	http.StatusUnauthorized:        bizerr.ErrCodeAuth,
	http.StatusServiceUnavailable:  bizerr.ErrCodeUnderMaintenance,
}

// ErrorHandlerUnaryServerInterceptor There are two sets of internal error code systems,
//
//	which need to be uniformly converted into OpenAPI error codes for unified external output
func ErrorHandlerUnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(
		ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
	) (interface{}, error) {
		resp, err := handler(ctx, req)
		if err == nil {
			return resp, err
		}

		if bizError, ok := err.(bizerr.BizError); ok {
			log.Info(ctx, "start to covert BizError error to bizErr format")
			return resp, status.Error(codes.Code(bizError.ErrCode()), bizError.ErrMsg())
		}
		if ec, ok := err.(errcode.ErrorCode); ok {
			log.Info(ctx, "start to covert errorcode error to bizErr format")
			httpCode := ec.Code().HTTPCode()
			code, ok := httpCode2BizErrorCodeMapping[httpCode]
			if !ok {
				return resp, err
			}
			return resp, status.Error(codes.Code(code), ec.Error())
		}
		return resp, err
	}
}
