package main

import (
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strings"

	sitter "github.com/smacker/go-tree-sitter"
	golang "github.com/smacker/go-tree-sitter/golang"
)

func main() {
	// Starting directory (you can modify this to any path)
	startDir := "."

	// Collect all Go files in the directory and its subdirectories
	goFiles := []string{}
	err := filepath.WalkDir(startDir, func(path string, d fs.DirEntry, err error) error {
		if !d.IsDir() && strings.HasSuffix(d.Name(), ".go") {
			goFiles = append(goFiles, path)
		}
		return nil
	})
	if err != nil {
		panic(err)
	}

	// Parse and summarize each Go file
	for _, filePath := range goFiles {
		parseAndSummarizeFile(filePath)
	}
}

func parseAndSummarizeFile(filePath string) {
	// Read the file
	source, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Printf("Error reading %s: %v\n", filePath, err)
		return
	}

	// Initialize the Tree-sitter parser
	parser := sitter.NewParser()
	parser.SetLanguage(golang.GetLanguage())

	// Parse the source code
	tree := parser.Parse(nil, source)
	defer tree.Close()

	// Print the structured summary
	printSummary(tree.RootNode(), source, filePath)
}

func printSummary(rootNode *sitter.Node, source []byte, fileName string) {
	relativePath, _ := filepath.Rel(".", fileName)
	fmt.Printf("\n%s:\n", relativePath)
	fmt.Println("⋮ ...")

	// Traverse to find top-level declarations
	cursor := sitter.NewTreeCursor(rootNode)
	defer cursor.Close()

	if !cursor.GoToFirstChild() {
		return
	}

	for {
		node := cursor.CurrentNode()
		switch node.Type() {
		case "type_declaration":
			printTypeDeclaration(node, source)
		case "function_declaration":
			printFunctionDeclaration(node, source, "")
		}

		if !cursor.GoToNextSibling() {
			break
		}
	}
}

func printTypeDeclaration(node *sitter.Node, source []byte) {
	typeSpecNode := node.ChildByFieldName("type")
	nameNode := node.ChildByFieldName("name")
	if typeSpecNode == nil || nameNode == nil {
		return
	}

	typeName := string(source[nameNode.StartByte():nameNode.EndByte()])
	typeKind := typeSpecNode.Type() // e.g., "struct_type", "interface_type"

	switch typeKind {
	case "struct_type":
		fmt.Printf("│ struct %s:\n", typeName)
		printStructFields(typeSpecNode, source)
	case "interface_type":
		fmt.Printf("│ interface %s:\n", typeName)
		printInterfaceMethods(typeSpecNode, source)
	default:
		fmt.Printf("│ type %s:\n", typeName)
	}
}

func printStructFields(node *sitter.Node, source []byte) {
	fieldList := node.ChildByFieldName("body")
	if fieldList == nil {
		return
	}

	for i := 0; i < int(fieldList.NamedChildCount()); i++ {
		field := fieldList.NamedChild(i)
		if field.Type() == "field_declaration" {
			namesNode := field.ChildByFieldName("name")
			if namesNode != nil {
				for j := 0; j < int(namesNode.NamedChildCount()); j++ {
					nameNode := namesNode.NamedChild(j)
					fieldName := string(source[nameNode.StartByte():nameNode.EndByte()])
					fmt.Printf("│   - %s\n", fieldName)
				}
			}
		}
	}
}

func printFunctionDeclaration(node *sitter.Node, source []byte, prefix string) {
	nameNode := node.ChildByFieldName("name")
	if nameNode == nil {
		return
	}
	funcName := string(source[nameNode.StartByte():nameNode.EndByte()])

	receiverNode := node.ChildByFieldName("receiver")
	if receiverNode != nil {
		// Method on a type
		receiverTypeNode := receiverNode.NamedChild(0).Child(1) // Assuming it's of the form (t *TypeName)
		typeName := ""
		if receiverTypeNode != nil {
			typeName = string(source[receiverTypeNode.StartByte():receiverTypeNode.EndByte()])
		}
		fmt.Printf("%s│ method %s.%s()\n", prefix, typeName, funcName)
	} else {
		// Regular function
		fmt.Printf("%s│ func %s()\n", prefix, funcName)
	}
}

func printInterfaceMethods(node *sitter.Node, source []byte) {
	methodList := node.ChildByFieldName("body")
	if methodList == nil {
		return
	}

	for i := 0; i < int(methodList.NamedChildCount()); i++ {
		method := methodList.NamedChild(i)
		if method.Type() == "method_spec" {
			nameNode := method.ChildByFieldName("name")
			if nameNode != nil {
				methodName := string(source[nameNode.StartByte():nameNode.EndByte()])
				fmt.Printf("│   - %s()\n", methodName)
			}
		}
	}
}


Right now it prints out:
example/example.go:
⋮ ...
│ func main()
│ func test()

example/test.go:
⋮ ...
│ func doSomething()

example/utils.go:
⋮ ...
│ func Helper()

byut for example it completely missed printing out interface information from same file 
type Printer interface {
	Print()
}

Also we should show parameters and reutrn values. take this as inspiration on how the output should look like:
aider/coders/base_coder.py:
⋮...
│class Coder:
│    abs_fnames = None
⋮...
│    @classmethod
│    def create(
│        self,
│        main_model,
│        edit_format,
│        io,
│        skip_model_availabily_check=False,
│        **kwargs,
⋮...
│    def abs_root_path(self, path):
⋮...
│    def run(self, with_message=None):
⋮...

aider/commands.py:
⋮...
│class Commands:
│    voice = None
│
⋮...
│    def get_commands(self):
⋮...
│    def get_command_completions(self, cmd_name, partial):
⋮...
│    def run(self, inp):
⋮...