package errs

import (
	"fmt"

	"gateway/internal/library/errors/code"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// GRPCCode maps the business category to a standard gRPC status code.
func (e *Error) GRPCCode() codes.Code {
	if e == nil || e.SubCode() == code.OK {
		return codes.OK
	}

	return grpcCodeForCategory(e.Category())
}

// GRPCStatus converts the error to a gRPC status.
// The business code is embedded in the message as [SSCCCDDD].
func (e *Error) GRPCStatus() *status.Status {
	if e == nil {
		return status.New(codes.OK, "")
	}

	msg := fmt.Sprintf("[%s] %s", e.DisplayCode(), e.Error())
	if cause := e.Unwrap(); cause != nil {
		msg = fmt.Sprintf("%s: %v", msg, cause)
	}

	return status.New(e.GRPCCode(), msg)
}

func grpcCodeForCategory(cat code.Category) codes.Code {
	switch cat {
	case code.InputInvalidFormat, code.InputNotValidImplementation, code.InputInvalidRange,
		code.InputMissingRequired, code.InputUnsupportedMedia,
		code.ResInsufficient, code.ResInvalidMeasureID, code.ResInvalidFormat,
		code.DBDataConvert, code.SvcHTTP400:
		return codes.InvalidArgument

	case code.InputPayloadTooLarge, code.PSuTooLarge:
		return codes.InvalidArgument

	case code.ResNotFound:
		return codes.NotFound

	case code.DBDuplicate, code.ResAlreadyExist, code.ResInvalidState, code.ResMultiOwner:
		return codes.AlreadyExists

	case code.ResPreconditionFailed, code.ResLocked:
		return codes.FailedPrecondition

	case code.AuthUnauthorized, code.AuthExpired, code.AuthInvalidPosixTime, code.AuthSigPayloadMismatch:
		return codes.Unauthenticated

	case code.AuthForbidden, code.ResInsufficientPerm:
		return codes.PermissionDenied

	case code.ResExpired, code.ResMigrated:
		return codes.FailedPrecondition

	case code.SysTooManyRequest, code.ResInsufficientQuota, code.SvcRateLimited:
		return codes.ResourceExhausted

	case code.SysTimeout:
		return codes.DeadlineExceeded

	case code.SysClientTimeout:
		return codes.Canceled

	case code.SysMaintain, code.SvcMaintenance, code.DBUnavailable:
		return codes.Unavailable

	case code.SvcThirdParty, code.PSuPublish, code.PSuConsume:
		return codes.Unavailable

	case code.SysNotImplemented, code.AuthMethodNotAllowed:
		return codes.Unimplemented

	case code.DBError, code.SysInternal, code.SvcInternal:
		return codes.Internal

	case code.CatGRPC:
		return codes.Unknown

	default:
		return codes.Unknown
	}
}
