package errors

import "fmt"

type Error struct {
	code  ErrorCode
	msg   string
	cause error
}

func New(code ErrorCode, msg string) Error {
	return Builder().Code(code).Message(msg).Build()
}

func NewErrorf(code ErrorCode, msgFormat string, a ...interface{}) Error {
	return New(code, fmt.Sprintf(msgFormat, a...))
}

func Wrap(code ErrorCode, msg string, err error) Error {
	return Builder().Code(code).Message(msg).Cause(err).Build()
}

func Is(err error, code ErrorCode) bool {
	if err == nil {
		return false
	}
	e, ok := err.(Error)
	return ok && e.Code() == code
}

func Ignore(err error, code ErrorCode) error {
	if Is(err, code) {
		return nil
	}
	return err
}

func (e Error) Error() string {
	errInfo := fmt.Sprintf("[%d]", e.Code())
	if len(e.msg) > 0 {
		errInfo = fmt.Sprintf("%s%s", errInfo, e.msg)
	} else {
		errInfo = fmt.Sprintf("%s%s", errInfo, e.Explain())
	}

	if e.cause != nil {
		errInfo = fmt.Sprintf("%s	cause: %s", errInfo, e.cause.Error())
	}
	return errInfo
}

func (e Error) Code() ErrorCode {
	return e.code
}

func (e Error) Explain() string {
	return e.code.Explain()
}

type ErrorBuilder struct {
	template Error
}

// Builder
// @return ErrorBuilder
func Builder() ErrorBuilder {
	return ErrorBuilder{
		template: Error{},
	}
}
func (t ErrorBuilder) Code(code ErrorCode) ErrorBuilder {
	t.template.code = code
	return t
}

func (t ErrorBuilder) Message(msg string) ErrorBuilder {
	t.template.msg = msg
	return t
}

func (t ErrorBuilder) Cause(cause error) ErrorBuilder {
	t.template.cause = cause
	return t
}

// Build
// @Description: the only way to get a efficient Error
// @Receiver t
// @return Error
func (t ErrorBuilder) Build() Error {
	return t.template
}

// IgnoreBy any errors that satisfy the supplied function by returning nil.
// Errors that do not satisfy the supplied function are returned unmodified.
func IgnoreBy(is func(error) bool, err error) error {
	if is(err) {
		return nil
	}
	return err
}
