package logging

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"regexp"
	"strconv"
	"strings"
)

type stackFrame struct {
	File string
	Line int
	Func string
}

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

func finalApplicationCaller(stack string) (stackFrame, bool) {
	if stack == "" {
		return stackFrame{}, false
	}
	functionName := ""
	for _, raw := range strings.Split(stack, "\n") {
		line := strings.TrimSpace(raw)
		if line == "" {
			continue
		}
		matches := stackFileLinePattern.FindStringSubmatch(line)
		if matches == nil {
			functionName = line
			continue
		}
		lineNumber, _ := strconv.Atoi(matches[2])
		if skipStackFile(matches[1]) {
			functionName = ""
			continue
		}
		return stackFrame{File: matches[1], Line: lineNumber, Func: functionName}, true
	}
	return stackFrame{}, false
}

func skipStackFile(filename string) bool {
	normalized := strings.ReplaceAll(filename, "\\", "/")
	for _, marker := range []string{
		"/go/pkg/mod/",
		"/go.uber.org/",
		"/gorm.io/",
		"/pkg/logging/",
		"/internal/transport/middleware/",
		"/internal/transport/router/",
		"/internal/server/handler/",
		"/internal/server/middleware/",
		"/internal/server/router/",
		"/internal/server/httpx/",
		"/internal/server/middleware_",
		"/internal/server/route_",
	} {
		if strings.Contains(normalized, marker) {
			return true
		}
	}
	return strings.Contains(normalized, "/src/") &&
		(strings.Contains(normalized, "/go/go") || strings.Contains(normalized, "/go/src/"))
}

func functionSourceAt(filename string, line int) (name, source string, startLine, endLine int, err error) {
	content, err := os.ReadFile(filename)
	if err != nil {
		return "", "", 0, 0, fmt.Errorf("read file failed: %w", err)
	}
	files := token.NewFileSet()
	parsed, err := parser.ParseFile(files, filename, content, parser.ParseComments)
	if err != nil {
		return "", "", 0, 0, fmt.Errorf("parse file failed: %w", err)
	}
	var target *ast.FuncDecl
	ast.Inspect(parsed, func(node ast.Node) bool {
		declaration, ok := node.(*ast.FuncDecl)
		if !ok {
			return true
		}
		start := files.Position(declaration.Pos()).Line
		end := files.Position(declaration.End()).Line
		if line >= start && line <= end {
			target, startLine, endLine = declaration, start, end
			return false
		}
		return true
	})
	if target == nil {
		return "", "", 0, 0, fmt.Errorf("no function encloses line %d in %s", line, filename)
	}
	start := files.Position(target.Pos()).Offset
	end := files.Position(target.End()).Offset
	if start < 0 || end > len(content) || start >= end {
		return "", "", 0, 0, fmt.Errorf("invalid offsets for function: start=%d end=%d len=%d", start, end, len(content))
	}
	return target.Name.Name, string(content[start:end]), startLine, endLine, nil
}
