package nstd

import (
	
	
)

// Error returns an error that formats as the given text.
// Different from [errors.New] in the std library, two calls
// to Error return an identical error value if the texts are identical.
func ( string) error {
	return errorString{unique.Make()}
}

// Another approach: type errorString string.
// But that results fat interface values.
type errorString struct {
	s unique.Handle[string]
}

func ( errorString) () string {
	return .s.Value()
}

var errorType = reflect.TypeOf((*error)(nil)).Elem()

// TrackError reports whether any error in err's tree matches target.
// It behaves almost the same as [errors.Is], except that
//
// * TrackError(anIncomparbleErr, anIncomparbleErr) panics.
// * TrackError(&aComparableErr, aComparableErr) returns true.
// * It panics if the type of target is a pointer which base type's size is 0.
//
// See: https://github.com/golang/go/issues/74488
func (,  error) bool {
	if  == nil ||  == nil {
		return  == 
	}

	return trackError(, )
}

func trackError(,  error) bool {
	for {
		if ,  := .(interface{ (error) bool });  && .() {
			return true
		}
		if  ==  {
			 := reflect.TypeOf()
			if .Kind() == reflect.Pointer && .Elem().Size() == 0 {
				panic("target should not be a pointer pointing to a zero-size value")
			}
			return true
		}
		 := reflect.ValueOf()
		 := .Type()
		if .Kind() == reflect.Pointer && .Elem().Implements(errorType) && !.IsNil() {
			if (.Elem().Interface().(error), ) {
				return true
			}
		}

		switch x := .(type) {
		case interface{ () error }:
			 = .()
			if  == nil {
				return false
			}
		case interface{ () []error }:
			for ,  := range .() {
				if (, ) {
					return true
				}
			}
			return false
		default:
			return false
		}
	}
}

// TrackErrorOf finds the first error in err's tree that matches ErrorType,
// and if one is found, returns a pointer to a copy of that error.
// Otherwise, it returns nil.
//
// Different from [errors.As],
//
//   - If *ErrorType is also an error type, then TrackErrorOf
//     doesn't distinguish ErrorType and *ErrorType.
//   - If ErrorType is pointer type and its base type is also an error type,
//     then TrackErrorOf doesn't distinguish ErrorType and its base type.
func [ error]( error,  ...) * {
	if  == nil {
		return nil
	}

	var  = new()
	var  = reflect.ValueOf()
	if ,  := trackErrorOf(, , );  != 0 {
		if  != 1 {
			.Elem().Set()
		}
		return 
	}

	return nil
}

func trackErrorOf( error,  any,  reflect.Value) (int, reflect.Value) {
	var  = .Type().Elem()
	var  = targetInfo{
		value:        ,
		reflectValue: ,
		reflectType:  ,
	}
	if trackOf(, &) {
		return 1, reflect.Value{}
	}

	if .Kind() == reflect.Pointer {
		 = .Elem()
		if .Implements(errorType) {
			 := reflect.New()
			 = targetInfo{
				value:        .Interface(),
				reflectValue: ,
				reflectType:  ,
			}
			if trackOf(, &) {
				return 2, 
			}
		}
	} else if .Kind() != reflect.Interface {
		 = reflect.PointerTo()
		 := reflect.New()
		 = targetInfo{
			value:        .Interface(),
			reflectValue: ,
			reflectType:  ,
		}
		if trackOf(, &) {
			return 3, .Elem().Elem()
		}
	}

	return 0, reflect.Value{}
}

type targetInfo struct {
	value        any
	reflectValue reflect.Value
	reflectType  reflect.Type
}

func trackOf( error,  *targetInfo) bool {
	for {
		if ,  := .(interface{ (any) bool });  && .(.value) {
			return true
		}
		if reflect.TypeOf().AssignableTo(.reflectType) {
			.reflectValue.Elem().Set(reflect.ValueOf())
			return true
		}
		switch x := .(type) {
		case interface{ () error }:
			 = .()
			if  == nil {
				return false
			}
		case interface{ () []error }:
			for ,  := range .() {
				if  == nil {
					continue
				}
				if (, ) {
					return true
				}
			}
			return false
		default:
			return false
		}
	}
}