package exerr

import "fmt"

// IsType test if the supplied error is of the specified ErrorType.
func IsType(err error, errType ErrorType) bool {
	if err == nil {
		return false
	}

	bmerr := FromError(err)
	for bmerr != nil {
		if bmerr.Type == errType {
			return true
		}
		bmerr = bmerr.OriginalError
	}

	return false
}

// IsFrom test if the supplied error stems originally from original
func IsFrom(e error, original error) bool {
	if e == nil {
		return false
	}

	//goland:noinspection GoDirectComparisonOfErrors
	if e == original {
		return true
	}

	bmerr := FromError(e)
	for bmerr == nil {
		return false
	}

	for curr := bmerr; curr != nil; curr = curr.OriginalError {
		if curr.Category == CatForeign && curr.Message == original.Error() && curr.WrappedErrType == fmt.Sprintf("%T", original) {
			return true
		}
	}

	return false
}

// HasSourceMessage tests if the supplied error stems originally from an error with the message msg
func HasSourceMessage(e error, msg string) bool {
	if e == nil {
		return false
	}

	bmerr := FromError(e)
	for bmerr == nil {
		return false
	}

	for curr := bmerr; curr != nil; curr = curr.OriginalError {
		if curr.OriginalError == nil && curr.Message == msg {
			return true
		}
	}

	return false
}

func MessageMatch(e error, matcher func(string) bool) bool {
	if e == nil {
		return false
	}

	if matcher(e.Error()) {
		return true
	}

	bmerr := FromError(e)
	for bmerr == nil {
		return false
	}

	for curr := bmerr; curr != nil; curr = curr.OriginalError {
		if matcher(curr.Message) {
			return true
		}
	}

	return false
}

// OriginalError returns the lowest level error, probably the original/external error that was originally wrapped
func OriginalError(e error) error {
	if e == nil {
		return nil
	}

	//goland:noinspection GoTypeAssertionOnErrors
	bmerr, ok := e.(*ExErr)
	for !ok {
		return e
	}

	for bmerr.OriginalError != nil {
		bmerr = bmerr.OriginalError
	}

	if bmerr.WrappedErr != nil {
		if werr, ok := bmerr.WrappedErr.(error); ok {
			return werr
		}
	}

	return bmerr
}