package errors

import (
	
	
	
	
	
	
)

var (
	// The function used to encode errors and their tags.
	Encoder func(any) ([]byte, error)
)

func init() {
	SetInlineEncoder()
}

// SetInlineEncoder is a helper function that set the error encoder to
// json.Marshal.
func () {
	Encoder = json.Marshal
}

// SetIndentEncoder is a helper function that set the error encoder to a
// function that uses json.MarshalIndent.
func () {
	Encoder = func( any) ([]byte, error) {
		return json.MarshalIndent(, "", "  ")
	}
}

// Unwrap returns the result of calling the Unwrap method on err, if err's type
// contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func ( error) error {
	return errors.Unwrap()
}

// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained
// by repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
//
// An error type might provide an Is method so it can be treated as equivalent
// to an existing error. For example, if MyError defines
//
//	func (m MyError) Is(target error) bool { return target == fs.ErrExist }
//
// then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for an
// example in the standard library. An Is method should only shallowly compare
// err and the target and not call Unwrap on either.
func (,  error) bool {
	return errors.Is(, )
}

// As finds the first error in err's chain that matches target, and if one is
// found, sets target to that error value and returns true. Otherwise, it
// returns false.
//
// The chain consists of err itself followed by the sequence of errors obtained
// by repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the
// value pointed to by target, or if the error has a method As(any) bool
// such that As(target) returns true. In the latter case, the As method is
// responsible for setting target.
//
// An error type might provide an As method so it can be treated as if it were a
// different error type.
//
// As panics if target is not a non-nil pointer to either a type that implements
// error, or to any interface type.
func ( error,  any) bool {
	return errors.As(, )
}

// Type returns the type of the error.
//
// Uses reflect.TypeOf() when the given error does not implements the Error
// interface.
func ( error) string {
	if  == nil {
		return ""
	}

	if ,  := .(interface{ () string });  {
		return .()
	}

	return reflect.TypeOf().String()
}

// HasType reports whether any error in err's chain matches the given type.
//
// The chain consists of err itself followed by the sequence of errors obtained
// by repeatedly calling Unwrap.
//
// An error matches the given type if the error has a method Type() string such
// that Type() returns a string equal to the given type.
func ( error,  string) bool {
	for {
		if  == Type() {
			return true
		}

		if  = Unwrap();  == nil {
			return false
		}
	}
}

// Tag returns the first tag value in err's chain that matches the given key.
//
// The chain consists of err itself followed by the sequence of errors obtained
// by repeatedly calling Unwrap.
//
// An error has a tag when it has a method Tag(string) string such that Tag(k)
// returns a non-empty string value.
func ( error,  string) any {
	for {
		if ,  := .(Error);  {
			if  := .Tag();  != nil {
				return 
			}
		}

		if  = Unwrap();  == nil {
			return nil
		}
	}
}

// An enriched error.
type Error struct {
	Line        string
	Message     string
	DefinedType string
	Tags        map[string]any
	WrappedErr  error
}

// New returns an error with the given message that can be enriched with a type
// and tags.
func ( string) Error {
	return makeError()
}

// Newf returns an error with the given formatted message that can be enriched
// with a type and tags.
func ( string,  ...any) Error {
	return makeError(, ...)
}

func makeError( string,  ...any) Error {
	, , ,  := runtime.Caller(2)

	 := Error{
		Line:    fmt.Sprintf("%s:%v", filepath.Base(), ),
		Message: fmt.Sprintf(, ...),
	}
	return 
}

func ( Error) ( string) Error {
	.DefinedType = 
	return 
}

func ( Error) () string {
	if .DefinedType != "" {
		return .DefinedType
	}

	if .WrappedErr != nil {
		return Type(.WrappedErr)
	}

	return reflect.TypeOf().String()
}

func ( Error) ( string,  any) Error {
	if .Tags == nil {
		.Tags = make(map[string]any)
	}

	.Tags[] = 
	return 
}

func ( Error) ( string) any {
	return .Tags[]
}

func ( Error) ( error) Error {
	.WrappedErr = 
	return 
}

func ( Error) () error {
	return .WrappedErr
}

func ( Error) () string {
	,  := Encoder()
	if  != nil {
		return fmt.Sprintf(`{"message": "encoding error failed: %s"}`, )
	}
	return string()
}

func ( Error) () ([]byte, error) {
	var  any = .WrappedErr
	if ,  := .WrappedErr.(Error); ! && .WrappedErr != nil {
		 = .WrappedErr.Error()
	}

	var  map[string]any
	if  := len(.Tags);  != 0 {
		 = make(map[string]any, )
		for ,  := range .Tags {
			switch v := .(type) {
			case reflect.Type:
				[] = .String()

			default:
				[] = 
			}
		}
	}

	return Encoder(struct {
		        string         `json:"line,omitempty"`
		     string         `json:"message"`
		 string         `json:"type,omitempty"`
		        map[string]any `json:"tags,omitempty"`
		  any            `json:"wrap,omitempty"`
	}{
		:        .Line,
		:     .Message,
		: .DefinedType,
		:        ,
		:  ,
	})
}

func ( Error) ( error) bool {
	,  := .(Error)
	if ! {
		return false
	}

	return .Line == .Line &&
		.Message == .Message &&
		.DefinedType == .DefinedType &&
		reflect.DeepEqual(.Tags, .Tags) &&
		.WrappedErr == .WrappedErr
}