package main

import (
	"errors"
	"fmt"
	"io"
)

// section: code
// Advanced error chain traversal patterns demonstrate
// how to inspect and navigate complex error chains

type ValidationError struct {
	Field   string
	Message string
	err     error
}

func (v *ValidationError) Error() string {
	return fmt.Sprintf("validation error on field %s: %s", v.Field, v.Message)
}

func (v *ValidationError) Unwrap() error {
	return v.err
}

type DatabaseError struct {
	Query string
	err   error
}

func (d *DatabaseError) Error() string {
	return fmt.Sprintf("database error executing query: %s", d.Query)
}

func (d *DatabaseError) Unwrap() error {
	return d.err
}

// collectErrorChain traverses the entire error chain and returns all errors
func collectErrorChain(err error) []error {
	var chain []error
	for err != nil {
		chain = append(chain, err)
		err = errors.Unwrap(err)
	}
	return chain
}

// findErrorInChain searches for a specific error type in the chain
func findErrorInChain[T error](err error) (T, bool) {
	var target T
	for err != nil {
		if errors.As(err, &target) {
			return target, true
		}
		err = errors.Unwrap(err)
	}
	return target, false
}

// printErrorChain displays the entire error chain with depth indicators
func printErrorChain(err error) {
	chain := collectErrorChain(err)
	fmt.Println("Error Chain:")
	for i, e := range chain {
		indent := ""
		for j := 0; j < i; j++ {
			indent += "  "
		}
		fmt.Printf("%s[%d] %v\n", indent, i, e)
	}
}

func simulateError() error {
	// Start with a base error
	baseErr := io.ErrUnexpectedEOF

	// Wrap in a database error
	dbErr := &DatabaseError{
		Query: "SELECT * FROM users WHERE id = ?",
		err:   baseErr,
	}

	// Wrap in a validation error
	valErr := &ValidationError{
		Field:   "user_id",
		Message: "failed to fetch user",
		err:     dbErr,
	}

	// Wrap with additional context
	return fmt.Errorf("operation failed: %w", valErr)
}

func main() {
	err := simulateError()

	// Print the entire error chain
	printErrorChain(err)

	fmt.Println("\n--- Error Inspection ---")

	// Check for specific error in the chain
	if errors.Is(err, io.ErrUnexpectedEOF) {
		fmt.Println("✓ Chain contains io.ErrUnexpectedEOF")
	}

	// Extract specific error type from chain
	var dbErr *DatabaseError
	if errors.As(err, &dbErr) {
		fmt.Printf("✓ Found DatabaseError with query: %s\n", dbErr.Query)
	}

	var valErr *ValidationError
	if errors.As(err, &valErr) {
		fmt.Printf("✓ Found ValidationError on field: %s\n", valErr.Field)
	}

	fmt.Println("\n--- Manual Chain Traversal ---")
	// Manually walk the chain
	currentErr := err
	depth := 0
	for currentErr != nil {
		fmt.Printf("Level %d: %T\n", depth, currentErr)
		currentErr = errors.Unwrap(currentErr)
		depth++
	}
}

// section: code

/*
// section: output
Error Chain:
[0] operation failed: validation error on field user_id: failed to fetch user
  [1] validation error on field user_id: failed to fetch user
    [2] database error executing query: SELECT * FROM users WHERE id = ?
      [3] unexpected EOF

--- Error Inspection ---
✓ Chain contains io.ErrUnexpectedEOF
✓ Found DatabaseError with query: SELECT * FROM users WHERE id = ?
✓ Found ValidationError on field: user_id

--- Manual Chain Traversal ---
Level 0: *fmt.wrapError
Level 1: *main.ValidationError
Level 2: *main.DatabaseError
Level 3: *errors.errorString
// section: output
*/
