package service

import (
	stderrors "errors"

	"github.com/pingcap/errors"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type grpcCodeError struct {
	code codes.Code
	err  error
}

func (e *grpcCodeError) Error() string {
	return e.err.Error()
}

func (e *grpcCodeError) Unwrap() error {
	return e.err
}

func (e *grpcCodeError) Cause() error {
	return e.err
}

func newGRPCCodeError(code codes.Code, message string) error {
	return &grpcCodeError{code: code, err: errors.New(message)}
}

func newGRPCCodeErrorf(code codes.Code, format string, args ...interface{}) error {
	return &grpcCodeError{code: code, err: errors.Errorf(format, args...)}
}

func wrapGRPCCodeError(code codes.Code, err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}
	return &grpcCodeError{code: code, err: errors.Wrapf(err, format, args...)}
}

func grpcCodeOfError(err error) codes.Code {
	if err == nil {
		return codes.OK
	}
	if s, ok := status.FromError(err); ok {
		return s.Code()
	}
	var codeErr *grpcCodeError
	if stderrors.As(err, &codeErr) {
		return codeErr.code
	}
	return codes.Unknown
}

func toStatusError(err error) error {
	if err == nil {
		return nil
	}
	if _, ok := status.FromError(err); ok {
		return err
	}
	if code := grpcCodeOfError(err); code != codes.Unknown && code != codes.OK {
		return status.Error(code, err.Error())
	}
	return status.Error(codes.Internal, err.Error())
}
