package errors

import (
	"fmt"
)

type BLError struct {
	code  BL_ERROR_CODE
	msg   string
	cause error
}

func Error(code BL_ERROR_CODE) BLError {
	return ErrorBuilder().Code(code).Build()
}

func NewError(code BL_ERROR_CODE, msg string) BLError {
	return ErrorBuilder().Code(code).Message(msg).Build()
}

func NewErrorf(code BL_ERROR_CODE, format string, a ...interface{}) BLError {
	return ErrorBuilder().Code(code).Message(fmt.Sprintf(format, a...)).Build()
}

func WrapError(code BL_ERROR_CODE, msg string, err error) BLError {
	return ErrorBuilder().Code(code).Message(msg).Cause(err).Build()
}

func (e BLError) Is(err error) bool {
	if e == err {
		return true
	}

	if e.cause == nil {
		return false
	}

	if cause, ok := e.cause.(BLError); ok {
		return cause.Is(err)
	} else {
		return e.cause == err
	}

}

func (e BLError) Error() string {
	errInfo := fmt.Sprintf("[%d] %s", e.GetCode(), e.GetCodeText())
	if len(e.msg) > 0 {
		errInfo = fmt.Sprintf("%s	%s", errInfo, e.msg)
	}

	if e.cause != nil {
		errInfo = fmt.Sprintf("%s	cause: %s", errInfo, e.cause.Error())
	}
	return errInfo
}

type BLErrorBuilder struct {
	template BLError
}

// ErrorBuilder
// @return BLErrorBuilder
func ErrorBuilder() BLErrorBuilder {
	return BLErrorBuilder{
		template: BLError{},
	}
}

func (t BLErrorBuilder) Code(code BL_ERROR_CODE) BLErrorBuilder {
	t.template.code = code
	return t
}

func (t BLErrorBuilder) Message(msg string) BLErrorBuilder {
	t.template.msg = msg
	return t
}

func (t BLErrorBuilder) Format(format string, args ...interface{}) BLErrorBuilder {
	t.template.msg = fmt.Sprintf(format, args...)
	return t
}

func (t BLErrorBuilder) Cause(cause error) BLErrorBuilder {
	t.template.cause = cause
	return t
}

// Build
// @Description: the only way to get a efficient BLError
// @Receiver t
// @return BLError
func (t BLErrorBuilder) Build() BLError {
	return t.template
}

func (e BLError) GetCode() BL_ERROR_CODE {
	return e.code
}

func (e BLError) GetMsg() string {
	if len(e.msg) == 0 {
		return e.GetCodeText()
	}
	return e.msg
}

func (e BLError) GetCodeText() string {
	return e.code.Explain()
}

func (e BLError) GetCause() error {
	return e.cause
}

func (e BLError) Unwrap() error {
	return e.cause
}
