package grpcerror

import (
	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/genproto/googleapis/rpc/errdetails"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protojson"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/consts"
)

var (
	// marshalOptions is a configurable JSON format marshaller.
	marshalOptions = protojson.MarshalOptions{
		// UseProtoNames:   true,
		EmitUnpopulated: true,
	}
)

func NewOpenAPIError(ctx *gin.Context, code int, err error) {
	s := status.New(codes.Code(code), err.Error())

	// add trace id
	s, _ = s.WithDetails(&errdetails.RequestInfo{
		RequestId: consts.GetTraceID(ctx.Request.Context()),
	})

	// Set status code before writing response body
	ctx.Status(code)
	ctx.Writer.Header().Set("Content-Type", "application/json")
	pb := s.Proto()
	buf, err := marshalOptions.Marshal(pb)
	if err != nil {
		tilog.Warn(ctx.Request.Context(), "failed to marshal grpc status error", zap.Error(err))
		if _, err := ctx.Writer.WriteString(consts.Fallback); err != nil {
			tilog.Warn(ctx.Request.Context(), "failed to write fallback response", zap.Error(err))
		}
		ctx.Abort()
		return
	}
	if _, err := ctx.Writer.Write(buf); err != nil {
		tilog.Warn(ctx.Request.Context(), "failed to write error status response", zap.Error(err))
	}
	ctx.Abort()
}
