// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package astutil_test

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/tools/internal/astutil"
)

func TestPreorderStack(t *testing.T) {
	const src = `package a
func f() {
	print("hello")
}
func g() {
	print("goodbye")
	panic("oops")
}
`
	fset := token.NewFileSet()
	f, _ := parser.ParseFile(fset, "a.go", src, 0)

	str := func(n ast.Node) string {
		return strings.TrimPrefix(reflect.TypeOf(n).String(), "*ast.")
	}

	var events []string
	var gotStack []string
	astutil.PreorderStack(f, nil, func(n ast.Node, stack []ast.Node) bool {
		events = append(events, str(n))
		if decl, ok := n.(*ast.FuncDecl); ok && decl.Name.Name == "f" {
			return false // skip subtree of f()
		}
		if lit, ok := n.(*ast.BasicLit); ok && lit.Value == `"oops"` {
			for _, n := range stack {
				gotStack = append(gotStack, str(n))
			}
		}
		return true
	})

	// Check sequence of events.
	const wantEvents = `[File Ident ` + // package a
		`FuncDecl ` + // func f()  [pruned]
		`FuncDecl Ident FuncType FieldList BlockStmt ` + // func g()
		`ExprStmt CallExpr Ident BasicLit ` + // print...
		`ExprStmt CallExpr Ident BasicLit]` // panic...
	if got := fmt.Sprint(events); got != wantEvents {
		t.Errorf("PreorderStack events:\ngot:  %s\nwant: %s", got, wantEvents)
	}

	// Check captured stack.
	const wantStack = `[File FuncDecl BlockStmt ExprStmt CallExpr]`
	if got := fmt.Sprint(gotStack); got != wantStack {
		t.Errorf("PreorderStack stack:\ngot:  %s\nwant: %s", got, wantStack)
	}
}
