// Package errors provides a robust error handling library with support for
// error wrapping, stack traces, context storage, and retry mechanisms. It extends
// the standard library's error interface with features like HTTP-like status codes,
// error categorization, and JSON serialization, while maintaining compatibility
// with `errors.Is`, `errors.As`, and `errors.Unwrap`. The package is thread-safe
// and optimized with object pooling for performance.
package errors

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
)

// Error is a custom error type with enhanced features: message, name, stack trace,
// context, cause, and metadata like code and category. It is thread-safe and
// supports pooling for performance.
type Error struct {
	// Fields used in atomic operations. Place them at the beginning of the
	// struct to ensure proper alignment across all architectures.
	count uint64 // Occurrence count for tracking frequency.

	// Primary fields (frequently accessed).
	msg   string    // The error message displayed by Error().
	name  string    // The error name or type (e.g., "AuthError").
	stack []uintptr // Stack trace as program counters.

	// Secondary metadata.
	template   string // Fallback message template if msg is empty.
	category   string // Error category (e.g., "network").
	code       int32  // HTTP-like status code (e.g., 400, 500).
	smallCount int32  // Number of items in smallContext.

	// Context and chaining.
	context      map[string]interface{}   // Key-value pairs for additional context.
	cause        error                    // Wrapped underlying error for chaining.
	callback     func()                   // Optional callback invoked by Error().
	smallContext [contextSize]contextItem // Fixed-size array for small contexts.

	// Synchronization.
	mu sync.RWMutex // Protects mutable fields (context, smallContext).

	// Internal flags.
	formatWrapped bool // True if created by Newf with %w verb.
}

// newError creates a new Error instance, reusing from the pool if enabled.
// Initializes smallContext and sets stack to nil.
// Internal use; prefer New, Named, or Trace for public API.
func newError() *Error {
	if currentConfig.disablePooling {
		return &Error{
			smallContext: [contextSize]contextItem{},
			stack:        nil,
		}
	}
	return errorPool.Get()
}

// Empty returns a new empty error with no message, name, or stack trace.
// Useful for incrementally building errors or as a neutral base.
// Example:
//
//	err := errors.Empty().With("key", "value").WithCode(400)
func Empty() *Error {
	return newError()
}

// Named creates an error with the specified name and captures a stack trace.
// The name doubles as the error message if no message is set.
// Use for errors where type identification and stack context are important.
// Example:
//
//	err := errors.Named("AuthError").WithCode(401)
func Named(name string) *Error {
	e := newError()
	e.name = name
	return e.WithStack()
}

// New creates a lightweight error with the given message and no stack trace.
// Optimized for performance; use Trace() for stack traces.
// Returns a shared empty error for empty messages to reduce allocations.
// Example:
//
//	err := errors.New("invalid input")
func New(text string) *Error {
	if text == "" {
		return emptyError.Copy() // Avoid modifying shared instance.
	}
	err := newError()
	err.msg = text
	return err
}

// Newf creates a formatted error, supporting the %w verb for wrapping errors.
// If the format contains exactly one %w verb with a non-nil error argument,
// the error is wrapped as the cause. The final error message string generated
// by Error() will be compatible with the output of fmt.Errorf for the same inputs.
// Does not capture a stack trace by default.
// Example:
//
//	cause := errors.New("db error")
//	err := errors.Newf("query failed: %w", cause)
//	// err.Error() will match fmt.Errorf("query failed: %w", cause).Error()
//	// errors.Unwrap(err) == cause
func Newf(f any, args ...interface{}) *Error {
	var format string
	switch v := f.(type) {
	case string:
		format = v
	case fmt.Stringer:
		format = v.String()
	default:
		panic("Newf: format must be a string or fmt.Stringer")
	}
	err := newError()

	var wCount int
	var wArgPos = -1
	var wArg error
	var validationErrorMsg string
	argPos := 0
	runes := []rune(format)
	i := 0
	parsingOk := true
	var fmtVerbs []struct {
		isW    bool
		spec   string // The full verb specifier or literal segment
		argIdx int    // Index in the original 'args' slice, -1 for literals/%%
	}

	// Parse format string to identify verbs and literals.
	for i < len(runes) && parsingOk {
		segmentStart := i
		if runes[i] == '%' {
			if i+1 >= len(runes) {
				parsingOk = false
				validationErrorMsg = "ends with %"
				break
			}
			if runes[i+1] == '%' {
				fmtVerbs = append(fmtVerbs, struct {
					isW    bool
					spec   string
					argIdx int
				}{isW: false, spec: "%%", argIdx: -1})
				i += 2
				continue
			}
			i++ // Move past '%'
			// Parse flags, width, precision (simplified loop)
			for i < len(runes) && strings.ContainsRune("+- #0", runes[i]) {
				i++
			}
			for i < len(runes) && ((runes[i] >= '0' && runes[i] <= '9') || runes[i] == '.') {
				i++
			}
			if i >= len(runes) {
				parsingOk = false
				validationErrorMsg = "ends mid-specifier"
				break
			}
			verb := runes[i]
			specifierEndIndex := i + 1
			fullSpec := string(runes[segmentStart:specifierEndIndex])
			// Check if the verb consumes an argument
			currentVerbConsumesArg := strings.ContainsRune("vTtbcdoqxXUeEfFgGspw", verb)
			currentArgIdx := -1
			isWVerb := false

			if verb == 'w' {
				isWVerb = true
				wCount++
				if wCount == 1 {
					wArgPos = argPos // Record position of the error argument
				} else {
					parsingOk = false
					validationErrorMsg = "multiple %w"
					break
				}
			}

			if currentVerbConsumesArg {
				if argPos >= len(args) {
					parsingOk = false
					if isWVerb { // More specific message for missing %w arg
						validationErrorMsg = "missing %w argument"
					} else {
						validationErrorMsg = fmt.Sprintf("missing argument for %s", string(verb))
					}
					break
				}
				currentArgIdx = argPos
				if isWVerb {
					cause, ok := args[argPos].(error)
					if !ok || cause == nil {
						parsingOk = false
						validationErrorMsg = "bad %w argument type"
						break
					}
					wArg = cause // Store the actual error argument
				}
				argPos++ // Consume the argument position
			}
			fmtVerbs = append(fmtVerbs, struct {
				isW    bool
				spec   string
				argIdx int
			}{isW: isWVerb, spec: fullSpec, argIdx: currentArgIdx})
			i = specifierEndIndex // Move past the verb character
		} else {
			// Handle literal segment
			literalStart := i
			for i < len(runes) && runes[i] != '%' {
				i++
			}
			fmtVerbs = append(fmtVerbs, struct {
				isW    bool
				spec   string
				argIdx int
			}{isW: false, spec: string(runes[literalStart:i]), argIdx: -1})
		}
	}

	// Check for too many arguments after parsing
	if parsingOk && argPos < len(args) {
		parsingOk = false
		validationErrorMsg = fmt.Sprintf("too many arguments for format %q", format)
	}

	// Handle format validation errors.
	if !parsingOk {
		switch validationErrorMsg {
		case "multiple %w":
			err.msg = fmt.Sprintf("errors.Newf: format %q has multiple %%w verbs", format)
		case "missing %w argument":
			err.msg = fmt.Sprintf("errors.Newf: format %q has %%w but not enough arguments", format)
		case "bad %w argument type":
			argValStr := "(<nil>)"
			if wArgPos >= 0 && wArgPos < len(args) && args[wArgPos] != nil {
				argValStr = fmt.Sprintf("(%T)", args[wArgPos])
			} else if wArgPos >= len(args) {
				argValStr = "(missing)" // Should be caught by "missing %w argument" case
			}
			err.msg = fmt.Sprintf("errors.Newf: argument %d for %%w is not a non-nil error %s", wArgPos, argValStr)
		case "ends with %":
			err.msg = fmt.Sprintf("errors.Newf: format %q ends with %%", format)
		case "ends mid-specifier":
			err.msg = fmt.Sprintf("errors.Newf: format %q ends during verb specifier", format)
		default: // Includes "too many arguments" and other potential fmt issues
			err.msg = fmt.Sprintf("errors.Newf: error in format %q: %s", format, validationErrorMsg)
		}
		err.cause = nil // Ensure no cause is set on format error
		err.formatWrapped = false
		return err
	}

	//  Start: Processing Valid Format String
	if wCount == 1 && wArg != nil {
		//  Handle %w: Simulate for Sprintf and pre-format
		err.cause = wArg         // Set the cause for unwrapping
		err.formatWrapped = true // Signal that msg is the final formatted string

		var finalFormat strings.Builder
		var finalArgs []interface{}
		causeStr := wArg.Error() // Get the string representation of the cause

		// Rebuild format string and argument list for Sprintf
		for _, verb := range fmtVerbs {
			if verb.isW {
				// Replace the %w verb specifier (e.g., "%w", "%+w") with "%s"
				finalFormat.WriteString("%s")
				// Add the cause's *string* to the arguments list for the new %s
				finalArgs = append(finalArgs, causeStr)
			} else {
				// Keep the original literal segment or non-%w verb specifier
				finalFormat.WriteString(verb.spec)
				if verb.argIdx != -1 {
					// Add the original argument for this non-%w verb/literal
					finalArgs = append(finalArgs, args[verb.argIdx])
				}
			}
		}

		// Format using the *modified* format string and arguments list
		result, fmtErr := FmtErrorCheck(finalFormat.String(), finalArgs...)
		if fmtErr != nil {
			// Handle potential errors during the final formatting step
			// This is unlikely if parsing passed, but possible with complex verbs/args
			err.msg = fmt.Sprintf("errors.Newf: formatting error during %%w simulation for format %q: %v", format, fmtErr)
			err.cause = nil // Don't keep the cause if final formatting failed
			err.formatWrapped = false
		} else {
			// Store the final, fully formatted string, matching fmt.Errorf output
			err.msg = result
		}
		//  End %w Simulation

	} else {
		//  No %w or wArg is nil: Format directly (original logic)
		result, fmtErr := FmtErrorCheck(format, args...)
		if fmtErr != nil {
			err.msg = fmt.Sprintf("errors.Newf: formatting error for format %q: %v", format, fmtErr)
			err.cause = nil
			err.formatWrapped = false
		} else {
			err.msg = result
			err.formatWrapped = false // Ensure false if no %w was involved
		}
	}
	//  End: Processing Valid Format String
	return err
}

// Errorf is an alias for Newf, providing a familiar interface compatible with
// fmt.Errorf. It creates a formatted error without capturing a stack trace.
// See Newf for full details on formatting, including %w support for error wrapping.
//
// Example:
//
//	err := errors.Errorf("failed: %w", errors.New("cause"))
//	// err.Error() == "failed: cause"

func Errorf(format string, args ...interface{}) *Error {
	return Newf(format, args...)
}

// Std creates a standard error using errors.New for compatibility.
// Does not capture stack traces or add context.
// Example:
//
//	err := errors.Std("simple error")
func Std(text string) error {
	return errors.New(text)
}

// Stdf creates a formatted standard error using fmt.Errorf for compatibility.
// Supports %w for wrapping; does not capture stack traces.
// Example:
//
//	err := errors.Stdf("failed: %w", cause)
func Stdf(format string, a ...interface{}) error {
	return fmt.Errorf(format, a...)
}

// Trace creates an error with the given message and captures a stack trace.
// Use when debugging context is needed; for performance, prefer New().
// Example:
//
//	err := errors.Trace("operation failed")
func Trace(text string) *Error {
	e := New(text)
	return e.WithStack()
}

// Tracef creates a formatted error with a stack trace.
// Supports %w for wrapping errors.
// Example:
//
//	err := errors.Tracef("query %s failed: %w", query, cause)
func Tracef(format string, args ...interface{}) *Error {
	e := Newf(format, args...)
	return e.WithStack()
}

// As attempts to assign the error or one in its chain to the target interface.
// Supports *Error and standard error types, traversing the cause chain.
// Returns true if successful.
// Example:
//
//	var target *Error
//	if errors.As(err, &target) {
//	  fmt.Println(target.Name())
//	}
func (e *Error) As(target interface{}) bool {
	if e == nil {
		return false
	}
	// Handle **Error target (i.e. caller passed &myErrPtr where myErrPtr is *Error).
	// Traverse the chain and return the first *Error that has a name; if none has a
	// name, return the first *Error in the chain. This satisfies both:
	//   - TestErrorAs: wraps Named("target") -> finds it by name
	//   - TestErrorFullChain: finds Named("AuthError") deep in the chain
	if targetPtr, ok := target.(**Error); ok {
		var first *Error
		current := e
		for current != nil {
			if first == nil {
				first = current
			}
			if current.name != "" {
				*targetPtr = current
				return true
			}
			if next, ok := current.cause.(*Error); ok {
				current = next
			} else if current.cause != nil {
				return errors.As(current.cause, target)
			} else {
				break
			}
		}
		if first != nil {
			*targetPtr = first
			return true
		}
		return false
	}
	// Handle *error target.
	if targetErr, ok := target.(*error); ok {
		innermost := error(e)
		current := error(e)
		for current != nil {
			if err, ok := current.(*Error); ok && err.cause != nil {
				current = err.cause
				innermost = current
			} else {
				break
			}
		}
		*targetErr = innermost
		return true
	}
	// Delegate to cause for other types.
	if e.cause != nil {
		return errors.As(e.cause, target)
	}
	return false
}

// Callback sets a function to be called when Error() is invoked.
// Useful for logging or side effects on error access.
// Example:
//
//	err := errors.New("test").Callback(func() { log.Println("error accessed") })
func (e *Error) Callback(fn func()) *Error {
	e.callback = fn
	return e
}

// Category returns the error’s category, if set.
// Example:
//
//	if err.Category() == "network" {
//	  handleNetworkError(err)
//	}
func (e *Error) Category() string {
	return e.category
}

// Code returns the error’s HTTP-like status code, if set.
// Returns 0 if no code is set.
// Example:
//
//	if err.Code() == 404 {
//	  renderNotFound()
//	}
func (e *Error) Code() int {
	return int(e.code)
}

// Context returns the error’s context as a map, merging smallContext and map-based context.
// Thread-safe; lazily initializes the map if needed.
// Example:
//
//	ctx := err.Context()
//	if userID, ok := ctx["user_id"]; ok {
//	  fmt.Println(userID)
//	}
func (e *Error) Context() map[string]interface{} {
	e.mu.RLock()
	defer e.mu.RUnlock()

	if e.smallCount > 0 && e.context == nil {
		e.context = make(map[string]interface{}, e.smallCount)
		for i := int32(0); i < e.smallCount; i++ {
			e.context[e.smallContext[i].key] = e.smallContext[i].value
		}
	}
	return e.context
}

// Copy creates a deep copy of the error, preserving all fields except stack freshness.
// The new error can be modified independently.
// Example:
//
//	newErr := err.Copy().With("new_key", "value")
func (e *Error) Copy() *Error {
	if e == emptyError {
		return &Error{
			smallContext: [contextSize]contextItem{},
		}
	}

	newErr := newError()

	newErr.msg = e.msg
	newErr.name = e.name
	newErr.template = e.template
	newErr.cause = e.cause
	newErr.code = e.code
	newErr.category = e.category
	newErr.count = e.count
	newErr.callback = e.callback           // was silently dropped by Copy
	newErr.formatWrapped = e.formatWrapped // was silently dropped by Copy

	if e.smallCount > 0 {
		newErr.smallCount = e.smallCount
		for i := int32(0); i < e.smallCount; i++ {
			newErr.smallContext[i] = e.smallContext[i]
		}
	} else if e.context != nil {
		newErr.context = make(map[string]interface{}, len(e.context))
		for k, v := range e.context {
			newErr.context[k] = v
		}
	}

	if e.stack != nil && len(e.stack) > 0 {
		if newErr.stack == nil {
			newErr.stack = stackPool.Get().([]uintptr)
		}
		newErr.stack = append(newErr.stack[:0], e.stack...)
	}

	return newErr
}

// Count returns the number of times the error has been incremented.
// Useful for tracking error frequency.
// Example:
//
//	fmt.Printf("Error occurred %d times", err.Count())
func (e *Error) Count() uint64 {
	return e.count
}

// Err returns the error as an error interface.
// Useful for type assertions or interface compatibility.
// Example:
//
//	var stdErr error = err.Err()
func (e *Error) Err() error {
	return e
}

// Error returns the string representation of the error.
// If the error was created using Newf/Errorf with the %w verb, it returns the
// pre-formatted string compatible with fmt.Errorf.
// Otherwise, it combines the message, template, or name with the cause's error
// string, separated by ": ". Invokes any set callback.
func (e *Error) Error() string {
	if e.callback != nil {
		e.callback()
	}

	// If created by Newf/Errorf with %w, msg already contains the final string.
	if e.formatWrapped {
		return e.msg // Return the pre-formatted fmt.Errorf-compatible string
	}

	//  Original logic for errors not created via Newf("%w", ...)
	//  or errors created via New/Named and then Wrap() called.
	var buf strings.Builder

	// Append primary message part (msg, template, or name)
	if e.msg != "" {
		buf.WriteString(e.msg)
	} else if e.template != "" {
		buf.WriteString(e.template)
	} else if e.name != "" {
		buf.WriteString(e.name)
	}

	// Append cause if it exists (only relevant if not formatWrapped)
	if e.cause != nil {
		if buf.Len() > 0 {
			// Add separator only if there was a prefix message/name/template
			buf.WriteString(": ")
		}
		buf.WriteString(e.cause.Error())
	} else if buf.Len() == 0 {
		// Handle case where msg/template/name are empty AND cause is nil
		// Could return a specific string like "[empty error]" or just ""
		return "" // Return empty string for a truly empty error
	}

	return buf.String()
}

// FastStack returns a lightweight stack trace with file and line numbers only.
// Omits function names for performance; skips internal frames if configured.
// Returns nil if no stack trace exists.
// Example:
//
//	for _, frame := range err.FastStack() {
//	  fmt.Println(frame) // e.g., "main.go:42"
//	}
func (e *Error) FastStack() []string {
	// Same len-vs-nil reasoning as Stack().
	if len(e.stack) == 0 {
		return nil
	}
	configMu.RLock()
	filter := currentConfig.filterInternal
	configMu.RUnlock()

	pcs := e.stack
	frames := make([]string, 0, len(pcs))
	for _, pc := range pcs {
		fn := runtime.FuncForPC(pc)
		if fn == nil {
			frames = append(frames, "unknown")
			continue
		}
		file, line := fn.FileLine(pc)
		if filter && isInternalFrame(runtime.Frame{File: file, Function: fn.Name()}) {
			continue
		}
		frames = append(frames, fmt.Sprintf("%s:%d", file, line))
	}
	return frames
}

// Find searches the error chain for the first error where pred returns true.
// Returns nil if no match is found or if pred is nil.
// Example:
//
//	err := err.Find(func(e error) bool { return strings.Contains(e.Error(), "timeout") })
func (e *Error) Find(pred func(error) bool) error {
	if e == nil || pred == nil {
		return nil
	}
	return Find(e, pred)
}

// Format returns a detailed, human-readable string representation of the error,
// including message, code, context, stack, and cause.
// Recursive for causes that are also *Error.
// Example:
//
//	fmt.Println(err.Format())
//	// Output:
//	// Error: failed: cause
//	// Code: 500
//	// Context:
//	//   key: value
//	// Stack:
//	//   1. main.main main.go:42
func (e *Error) Format() string {
	var sb strings.Builder

	// Error message.
	sb.WriteString("Error: " + e.Error() + "\n")

	// Metadata.
	if e.code != 0 {
		sb.WriteString(fmt.Sprintf("Code: %d\n", e.code))
	}

	// Context.
	if ctx := e.contextAtThisLevel(); len(ctx) > 0 {
		sb.WriteString("Context:\n")
		for k, v := range ctx {
			sb.WriteString(fmt.Sprintf("\t%s: %v\n", k, v))
		}
	}

	// Stack trace.
	if e.stack != nil {
		sb.WriteString("Stack:\n")
		for i, frame := range e.Stack() {
			sb.WriteString(fmt.Sprintf("\t%d. %s\n", i+1, frame))
		}
	}

	// Cause.
	if e.cause != nil {
		sb.WriteString("Caused by: ")
		if causeErr, ok := e.cause.(*Error); ok {
			sb.WriteString(causeErr.Format())
		} else {
			sb.WriteString("Error: " + e.cause.Error() + "\n")
		}
		sb.WriteString("\n")
	}

	return sb.String()
}

// contextAtThisLevel returns context specific to this error, excluding inherited context.
// Internal use by Format to isolate context per error level.
func (e *Error) contextAtThisLevel() map[string]interface{} {
	if e.context == nil && e.smallCount == 0 {
		return nil
	}

	ctx := make(map[string]interface{})
	// Add smallContext items.
	for i := 0; i < int(e.smallCount); i++ {
		ctx[e.smallContext[i].key] = e.smallContext[i].value
	}
	// Add map context items.
	if e.context != nil {
		for k, v := range e.context {
			ctx[k] = v
		}
	}
	return ctx
}

// Free resets the error and returns it to the pool if pooling is enabled.
// Safe to call multiple times; no-op if pooling is disabled.
// Call after use to return the error to the pool and prevent memory leaks.
// Use defer err.Free() at the call site that created the error.
// Example:
//
//	defer err.Free()
func (e *Error) Free() {
	if currentConfig.disablePooling {
		return
	}

	// Disarm any pending auto-cleanup (finalizer or runtime.AddCleanup) before
	// manually returning to the pool. Without this, GC could return the same
	// *Error a second time after Free() has already done so — double-put.
	errorPool.clearCleanup(e)

	e.Reset()

	if e.stack != nil {
		stackPool.Put(e.stack[:cap(e.stack)])
		e.stack = nil
	}
	errorPool.Put(e)
}

// Has checks if the error contains meaningful content (message, template, name, or cause).
// Returns false for nil or empty errors.
// Example:
//
//	if !err.Has() {
//	  return nil
//	}
func (e *Error) Has() bool {
	return e != nil && (e.msg != "" || e.template != "" || e.name != "" || e.cause != nil)
}

// HasContextKey checks if the specified key exists in the error’s context.
// Thread-safe; checks both smallContext and map-based context.
// Example:
//
//	if err.HasContextKey("user_id") {
//	  fmt.Println(err.Context()["user_id"])
//	}
func (e *Error) HasContextKey(key string) bool {
	e.mu.RLock()
	defer e.mu.RUnlock()

	if e.smallCount > 0 {
		for i := int32(0); i < e.smallCount; i++ {
			if e.smallContext[i].key == key {
				return true
			}
		}
	}
	if e.context != nil {
		_, exists := e.context[key]
		return exists
	}
	return false
}

// Increment atomically increases the error’s count by 1 and returns the error.
// Useful for tracking repeated occurrences.
// Example:
//
//	err := err.Increment()
func (e *Error) Increment() *Error {
	atomic.AddUint64(&e.count, 1)
	return e
}

// Is checks if the error matches the target by pointer, name, or cause chain.
// Compatible with errors.Is; also matches by string for standard errors.
// Returns true if the error or its cause matches the target.
// Example:
//
//	if errors.Is(err, errors.New("target")) {
//	  handleTargetError()
//	}
func (e *Error) Is(target error) bool {
	if e == nil || target == nil {
		return e == target
	}
	if e == target {
		return true
	}
	if e.name != "" {
		if te, ok := target.(*Error); ok && te.name != "" && e.name == te.name {
			return true
		}
	}
	// String-equality fallback: matches any error whose message equals this
	// error's message. This is intentional — it allows matching errors created
	// by fmt.Errorf or errors.New with the same text — but it deviates from
	// stdlib errors.Is which uses pointer/sentinel identity.
	// IMPORTANT: two distinct errors with identical messages will match each other.
	// For strict identity matching use errors.Const() to create named sentinels.
	if stdErr, ok := target.(error); ok && e.Error() == stdErr.Error() {
		return true
	}
	if e.cause != nil {
		return errors.Is(e.cause, target)
	}
	return false
}

// IsEmpty checks if the error lacks meaningful content (no message, name, template, or cause).
// Returns true for nil or fully empty errors.
// Example:
//
//	if err.IsEmpty() {
//	  return nil
//	}
func (e *Error) IsEmpty() bool {
	if e == nil {
		return true
	}
	return e.msg == "" && e.template == "" && e.name == "" && e.cause == nil
}

// IsNull checks if the error is nil, empty, or contains only SQL NULL values in its context or cause.
// Useful for handling database-related errors.
// Example:
//
//	if err.IsNull() {
//	  return nil
//	}
func (e *Error) IsNull() bool {
	if e == nil || e == emptyError {
		return true
	}
	// If no context or cause, and no content, it’s not null.
	if e.smallCount == 0 && e.context == nil && e.cause == nil {
		return false
	}

	// Check cause first.
	if e.cause != nil {
		var isNull bool
		if ce, ok := e.cause.(*Error); ok {
			isNull = ce.IsNull()
		} else {
			isNull = sqlNull(e.cause)
		}
		if isNull {
			return true
		}
	}

	// Check small context.
	if e.smallCount > 0 {
		allNull := true
		for i := 0; i < int(e.smallCount); i++ {
			isNull := sqlNull(e.smallContext[i].value)
			if !isNull {
				allNull = false
				break
			}
		}
		if !allNull {
			return false
		}
	}

	// Check regular context.
	if e.context != nil {
		allNull := true
		for _, v := range e.context {
			isNull := sqlNull(v)
			if !isNull {
				allNull = false
				break
			}
		}
		if !allNull {
			return false
		}
	}

	// Null if context exists and is all null.
	return e.smallCount > 0 || e.context != nil
}

// MarshalJSON serializes the error to JSON, including name, message, context, cause, stack, and code.
// Causes are recursively serialized if they implement json.Marshaler or are *Error.
// Example:
//
//	data, _ := json.Marshal(err)
//	fmt.Println(string(data))
func (e *Error) MarshalJSON() ([]byte, error) {
	// Get buffer from pool. Do NOT defer-return it — we must copy the result
	// out of buf's backing array and return the buf to the pool BEFORE we return
	// the copied slice. If we defer the Put, another goroutine can Get the same
	// buf and overwrite its backing array while the caller is still reading our
	// returned slice (the race the detector flags).
	buf := jsonBufferPool.Get().(*bytes.Buffer)
	buf.Reset()

	// Create new encoder.
	enc := json.NewEncoder(buf)
	enc.SetEscapeHTML(false)

	// Prepare JSON structure.
	je := struct {
		Name    string                 `json:"name,omitempty"`
		Message string                 `json:"message,omitempty"`
		Context map[string]interface{} `json:"context,omitempty"`
		Cause   interface{}            `json:"cause,omitempty"`
		Stack   []string               `json:"stack,omitempty"`
		Code    int                    `json:"code,omitempty"`
	}{
		Name:    e.name,
		Message: e.msg,
		Code:    e.Code(),
	}

	// Add context.
	if ctx := e.Context(); len(ctx) > 0 {
		je.Context = ctx
	}

	// Add stack.
	if e.stack != nil {
		je.Stack = e.Stack()
	}

	// Add cause.
	if e.cause != nil {
		switch c := e.cause.(type) {
		case *Error:
			je.Cause = c
		case json.Marshaler:
			je.Cause = c
		default:
			je.Cause = c.Error()
		}
	}

	// Encode JSON.
	if err := enc.Encode(je); err != nil {
		return nil, err
	}

	// Copy bytes out of buf before returning buf to the pool.
	// buf.Bytes() is a slice into buf's internal array — if we put buf back first
	// and another goroutine resets it, they share the same backing memory.
	raw := buf.Bytes()
	if len(raw) > 0 && raw[len(raw)-1] == '\n' {
		raw = raw[:len(raw)-1]
	}
	result := make([]byte, len(raw))
	copy(result, raw)
	jsonBufferPool.Put(buf)
	return result, nil
}

// Msgf sets the error’s message using a formatted string and returns the error.
// Overwrites any existing message.
// Example:
//
//	err := err.Msgf("user %s not found", username)
func (e *Error) Msgf(format string, args ...interface{}) *Error {
	e.msg = fmt.Sprintf(format, args...)
	return e
}

// Name returns the error’s name, if set.
// Example:
//
//	if err.Name() == "AuthError" {
//	  handleAuthError()
//	}
func (e *Error) Name() string {
	return e.name
}

// Reset clears all fields of the error, preparing it for reuse in the pool.
// Internal use by Free; does not release stack to stackPool.
// Example:
//
//	err.Reset() // Clear all fields.
func (e *Error) Reset() {
	e.msg = ""
	e.name = ""
	e.template = ""
	e.category = ""
	e.code = 0
	e.count = 0
	e.cause = nil
	e.callback = nil
	e.formatWrapped = false

	if e.context != nil {
		for k := range e.context {
			delete(e.context, k)
		}
	}
	e.smallCount = 0

	if e.stack != nil {
		e.stack = e.stack[:0]
	}
}

// Stack returns a detailed stack trace with function names, files, and line numbers.
// Filters internal frames if configured; returns nil if no stack exists.
// Example:
//
//	for _, frame := range err.Stack() {
//	  fmt.Println(frame) // e.g., "main.main main.go:42"
//	}
func (e *Error) Stack() []string {
	// Use len check not nil: a recycled error has stack reset to stack[:0]
	// (non-nil, zero length). Calling CallersFrames on an empty slice returns
	// no frames, making Stack() silently return [] instead of nil.
	if len(e.stack) == 0 {
		return nil
	}

	frames := runtime.CallersFrames(e.stack)
	var trace []string
	for {
		frame, more := frames.Next()
		if frame == (runtime.Frame{}) {
			break
		}

		if currentConfig.filterInternal && isInternalFrame(frame) {
			continue
		}

		trace = append(trace, fmt.Sprintf("%s %s:%d",
			frame.Function,
			frame.File,
			frame.Line))

		if !more {
			break
		}
	}
	return trace
}

// Trace ensures the error has a stack trace, capturing it if absent.
// Returns the error for chaining.
// Example:
//
//	err := errors.New("failed").Trace()
func (e *Error) Trace() *Error {
	// Check len rather than nil for the same reason as WithStack.
	if len(e.stack) == 0 {
		// skip=1: trimmed = skip+1 = 2, removes captureStack + Trace() itself.
		e.stack = captureStack(1)
	}
	return e
}

// Transform applies transformations to a copy of the error and returns the new error.
// The original error is unchanged; nil-safe.
// Example:
//
//	newErr := err.Transform(func(e *Error) { e.With("key", "value") })
func (e *Error) Transform(fn func(*Error)) *Error {
	if e == nil || fn == nil {
		return e
	}
	newErr := e.Copy()
	fn(newErr)
	return newErr
}

// Unwrap returns the underlying cause of the error, if any.
// Compatible with errors.Unwrap for chain traversal.
// Example:
//
//	cause := errors.Unwrap(err)
func (e *Error) Unwrap() error {
	return e.cause
}

// UnwrapAll returns a slice of all errors in the chain, starting with this error.
// Each error is isolated to prevent modifications affecting others.
// Example:
//
//	chain := err.UnwrapAll()
//	for _, e := range chain {
//	  fmt.Println(e.Error())
//	}
func (e *Error) UnwrapAll() []error {
	if e == nil {
		return nil
	}
	// Return the original nodes directly. Each *Error in the chain already owns
	// its own context map and message — there is no bleeding between nodes.
	// Returning originals (rather than copies) ensures:
	// e.Error() returns only that node's own msg/name (cause is on the
	//      NEXT node, not duplicated here — Error() appends e.cause.Error() which
	//      is exactly the next node's contribution).
	//
	// Wait — Error() DOES append cause. So chain[0].Error() includes the full
	// chain. The test wants chain[0].Error() == "outer" (msg only).
	// return snapshot wrappers that expose only the node's own message.
	var chain []error
	current := error(e)
	for current != nil {
		if err, ok := current.(*Error); ok {
			// Wrap in a msgOnlyError so Error() returns only this node's own
			// message without appending the cause chain. Unwrap() still returns
			// the original *Error so standard chain traversal continues to work.
			chain = append(chain, &msgOnlyError{err: err})
		} else {
			chain = append(chain, current)
		}
		if unwrapper, ok := current.(interface{ Unwrap() error }); ok {
			current = unwrapper.Unwrap()
		} else {
			break
		}
	}
	return chain
}

// Walk traverses the error chain, applying fn to each error.
// Stops if fn is nil or the chain ends.
// Example:
//
//	err.Walk(func(e error) { fmt.Println(e.Error()) })
func (e *Error) Walk(fn func(error)) {
	if e == nil || fn == nil {
		return
	}
	current := error(e)
	for current != nil {
		fn(current)
		if unwrappable, ok := current.(interface{ Unwrap() error }); ok {
			current = unwrappable.Unwrap()
		} else {
			break
		}
	}
}

// With adds key-value pairs to the error's context and returns the error.
// Uses a fixed-size array (smallContext) for up to contextSize items, then switches
// to a map. Thread-safe. Accepts variadic key-value pairs.
// Example:
//
//	err := err.With("key1", value1, "key2", value2)
func (e *Error) With(keyValues ...interface{}) *Error {
	if len(keyValues) == 0 {
		return e
	}

	// Validate that we have an even number of arguments
	if len(keyValues)%2 != 0 {
		keyValues = append(keyValues, "(MISSING)")
	}

	// Acquire the lock once up-front. The previous "optimistic read then lock"
	// pattern read e.smallCount and e.context without holding the lock, which
	// the race detector correctly flagged as a data race when two goroutines
	// call With() on the same *Error concurrently.
	e.mu.Lock()
	defer e.mu.Unlock()

	// Fast path: all pairs fit in the fixed-size smallContext array.
	if e.smallCount < contextSize && e.context == nil {
		remainingSlots := contextSize - int(e.smallCount)
		if len(keyValues)/2 <= remainingSlots {
			for i := 0; i < len(keyValues); i += 2 {
				key, ok := keyValues[i].(string)
				if !ok {
					key = fmt.Sprintf("%v", keyValues[i])
				}
				e.smallContext[e.smallCount] = contextItem{key, keyValues[i+1]}
				e.smallCount++
			}
			return e
		}
	}

	// Slow path: too many pairs or already using map context.

	// Initialize map context if needed
	if e.context == nil {
		e.context = make(map[string]interface{}, max(currentConfig.contextSize, len(keyValues)/2+int(e.smallCount)))
		// Migrate existing smallContext items
		for i := int32(0); i < e.smallCount; i++ {
			e.context[e.smallContext[i].key] = e.smallContext[i].value
		}
		// Reset smallCount since we've moved to map context
		e.smallCount = 0
	}

	// Add all pairs to map context
	for i := 0; i < len(keyValues); i += 2 {
		key, ok := keyValues[i].(string)
		if !ok {
			key = fmt.Sprintf("%v", keyValues[i])
		}
		e.context[key] = keyValues[i+1]
	}

	return e
}

// Helper function to get maximum of two integers
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// WithCategory sets the error’s category and returns the error.
// Example:
//
//	err := err.WithCategory("validation")
func (e *Error) WithCategory(category ErrorCategory) *Error {
	e.category = string(category)
	return e
}

// WithCode sets an HTTP-like status code and returns the error.
// Example:
//
//	err := err.WithCode(400)
func (e *Error) WithCode(code int) *Error {
	e.code = int32(code)
	return e
}

// WithName sets the error’s name and returns the error.
// Example:
//
//	err := err.WithName("AuthError")
func (e *Error) WithName(name string) *Error {
	e.name = name
	return e
}

// WithRetryable marks the error as retryable in its context and returns the error.
// Example:
//
//	err := err.WithRetryable()
func (e *Error) WithRetryable() *Error {
	return e.With(ctxRetry, true)
}

// WithStack captures a stack trace if none exists and returns the error.
// Skips one frame (caller of WithStack).
// Example:
//
//	err := errors.New("failed").WithStack()
func (e *Error) WithStack() *Error {
	// Check len rather than nil: a pooled error has stack reset to stack[:0]
	// (non-nil but empty). The nil check would skip capture for recycled errors.
	if len(e.stack) == 0 {
		e.stack = captureStack(1)
	}
	return e
}

// WithTemplate sets a message template and returns the error.
// Used as a fallback if the message is empty.
// Example:
//
//	err := err.WithTemplate("operation failed")
func (e *Error) WithTemplate(template string) *Error {
	e.template = template
	return e
}

// WithTimeout marks the error as a timeout error in its context and returns the error.
// Example:
//
//	err := err.WithTimeout()
func (e *Error) WithTimeout() *Error {
	return e.With(ctxTimeout, true)
}

// Wrap associates a cause error with this error, creating a chain.
// Returns the error unchanged if cause is nil.
// Example:
//
//	err := errors.New("failed").Wrap(errors.New("cause"))
func (e *Error) Wrap(cause error) *Error {
	if cause == nil {
		return e
	}
	e.cause = cause
	return e
}

// Wrapf wraps a cause error with formatted message and returns the error.
// If cause is nil, returns the error unchanged.
// Example:
//
//	err := errors.New("base").Wrapf(io.EOF, "read failed: %s", "file.txt")
func (e *Error) Wrapf(cause error, format string, args ...interface{}) *Error {
	e.msg = fmt.Sprintf(format, args...)
	if cause != nil {
		e.cause = cause
	}
	return e
}

// WrapNotNil wraps a cause error only if it is non-nil and returns the error.
// Example:
//
//	err := err.WrapNotNil(maybeError)
func (e *Error) WrapNotNil(cause error) *Error {
	if cause != nil {
		e.cause = cause
	}
	return e
}

// LogValue implements slog.LogValuer so *Error can be passed directly to
// any slog logging call and will be rendered as a structured group containing
// message, name, code, category, and context fields.
//
// Example:
//
//	slog.Error("request failed", "err", err)
//	// => err.message="...", err.name="AuthError", err.code=401, ...
func (e *Error) LogValue() slog.Value {
	if e == nil {
		return slog.StringValue("<nil>")
	}
	attrs := make([]slog.Attr, 0, 6)
	if e.msg != "" {
		attrs = append(attrs, slog.String("message", e.msg))
	}
	if e.name != "" {
		attrs = append(attrs, slog.String("name", e.name))
	}
	if e.code != 0 {
		attrs = append(attrs, slog.Int("code", int(e.code)))
	}
	if e.category != "" {
		attrs = append(attrs, slog.String("category", e.category))
	}
	if ctx := e.contextAtThisLevel(); len(ctx) > 0 {
		ctxAttrs := make([]slog.Attr, 0, len(ctx))
		for k, v := range ctx {
			ctxAttrs = append(ctxAttrs, slog.Any(k, v))
		}
		attrs = append(attrs, slog.Attr{Key: "context", Value: slog.GroupValue(ctxAttrs...)})
	}
	if e.cause != nil {
		attrs = append(attrs, slog.String("cause", e.cause.Error()))
	}
	return slog.GroupValue(attrs...)
}

// msgOnlyError wraps a single *Error and returns only its own message from
// Error(), without appending the cause chain. Used by UnwrapAll so each
// element in the returned slice represents exactly one chain node.
type msgOnlyError struct {
	err *Error
}

func (m *msgOnlyError) Error() string {
	if m.err.msg != "" {
		return m.err.msg
	}
	if m.err.name != "" {
		return m.err.name
	}
	if m.err.template != "" {
		return m.err.template
	}
	return ""
}

// Unwrap returns the underlying *Error so errors.Is/As and chain traversal work.
func (m *msgOnlyError) Unwrap() error { return m.err.cause }

// Convenience accessors so callers can still reach *Error fields after UnwrapAll.
func (m *msgOnlyError) Name() string                    { return m.err.Name() }
func (m *msgOnlyError) Code() int                       { return m.err.Code() }
func (m *msgOnlyError) Context() map[string]interface{} { return m.err.Context() }
func (m *msgOnlyError) Stack() []string                 { return m.err.Stack() }
