package stacktrace

import (
	"regexp"
	"strconv"
	"strings"
)

// Frame 表示一次栈帧解析结果
type Frame struct {
	File string
	Line int
	Func string
}

var fileLineRe = regexp.MustCompile(`\s*(.+\.go):(\d+)\s*`)

// FindFinalCaller 从 zap 的 entry.Stack 文本中，解析"最终业务调用方"的文件与行号
// 策略：自顶向下解析，优先选择第一条项目代码帧，过滤第三方库/标准库/框架中间件
func FindFinalCaller(stack string) (Frame, bool) {
	if stack == "" {
		return Frame{}, false
	}
	lines := strings.Split(stack, "\n")
	var currFunc string
	for i := 0; i < len(lines); i++ {
		line := strings.TrimSpace(lines[i])
		if line == "" {
			continue
		}
		if m := fileLineRe.FindStringSubmatch(line); m != nil {
			file := m[1]
			ln, _ := strconv.Atoi(m[2])
			if shouldSkip(file) {
				// 跳过此帧，同时重置函数名以避免错误配对
				currFunc = ""
				continue
			}
			return Frame{File: file, Line: ln, Func: currFunc}, true
		}
		// 记录函数名行，下一行通常是文件:行
		currFunc = line
	}
	return Frame{}, false
}

func shouldSkip(file string) bool {
	// 第三方库与 Go 模块缓存
	if strings.Contains(file, "/go/pkg/mod/") {
		return true
	}
	if strings.Contains(file, "/go.uber.org/") {
		return true
	}
	if strings.Contains(file, "/gorm.io/") {
		return true
	}
	// 标准库
	if strings.Contains(file, "/go/go") && strings.Contains(file, "/src/") {
		return true
	}
	// 框架内不需要作为最终调用方的路径
	if strings.Contains(file, "/internal/server/middleware/") {
		return true
	}
	if strings.Contains(file, "/internal/server/router/") {
		return true
	}
	if strings.Contains(file, "/pkg/utils/") {
		return true
	}
	return false
}
