package generic

import (
	"regexp"
	"strings"
	"unicode"

	"github.com/tidbcloud/AgentTeam/kits/agentteam-insight/internal/graphbuilder/extractor"
)

func extractTypeScriptJavaScript(analysis *extractor.StructuralAnalysis, text string) {
	addRegexImports(analysis, text, []importPattern{
		{regexp.MustCompile(`(?m)^\s*import\s+(?:type\s+)?(?:[^'";]+?\s+from\s+)?["']([^"']+)["']`), 1},
		{regexp.MustCompile(`(?m)^\s*export\s+(?:[^'";]+?\s+from\s+)["']([^"']+)["']`), 1},
		{regexp.MustCompile(`\brequire\(\s*["']([^"']+)["']\s*\)`), 1},
		{regexp.MustCompile(`\bimport\(\s*["']([^"']+)["']\s*\)`), 1},
	})
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(`)) {
		addFunction(analysis, match.Name, match.Line, isExportedLine(match.Text, match.Name))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>`)) {
		addFunction(analysis, match.Name, match.Line, isExportedLine(match.Text, match.Name))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:export\s+)?(?:default\s+)?class\s+([A-Za-z_$][\w$]*)`)) {
		addType(analysis, match.Name, "class", match.Line, isExportedLine(match.Text, match.Name))
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_$][\w$]*)\s*\(`), jsKeywords())
}

func extractPython(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:from\s+([A-Za-z0-9_\.]+)\s+import\s+([A-Za-z0-9_, *]+)|import\s+([A-Za-z0-9_\.]+)(?:\s+as\s+([A-Za-z0-9_]+))?)`)) {
		if match.Groups[0] != "" {
			analysis.Imports = append(analysis.Imports, extractor.Import{Path: match.Groups[0], Specifiers: splitSpecifiers(match.Groups[1])})
			continue
		}
		analysis.Imports = append(analysis.Imports, extractor.Import{Path: match.Groups[2], Alias: match.Groups[3]})
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(`)) {
		addFunction(analysis, match.Name, match.Line, !strings.HasPrefix(match.Name, "_"))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*class\s+([A-Za-z_]\w*)`)) {
		addType(analysis, match.Name, "class", match.Line, !strings.HasPrefix(match.Name, "_"))
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*)\s*\(`), pythonKeywords())
}

func extractRust(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:pub\s+)?use\s+([^;]+);`)) {
		analysis.Imports = append(analysis.Imports, extractor.Import{Path: normalizeRustPath(match.Groups[0])})
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:pub\s+)?mod\s+([A-Za-z_]\w*)\s*;`)) {
		analysis.Imports = append(analysis.Imports, extractor.Import{Path: match.Name})
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(pub\s+)?(?:async\s+)?fn\s+([A-Za-z_]\w*)\s*(?:<[^>]+>)?\s*\(`)) {
		addFunction(analysis, match.Groups[1], match.Line, strings.TrimSpace(match.Groups[0]) != "")
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(pub\s+)?(struct|enum|trait)\s+([A-Za-z_]\w*)`)) {
		addType(analysis, match.Groups[2], match.Groups[1], match.Line, strings.TrimSpace(match.Groups[0]) != "")
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*)\s*(?:::<[^>]+>)?\s*\(`), rustKeywords())
}

func extractJavaLike(analysis *extractor.StructuralAnalysis, text string, language string) {
	importRE := regexp.MustCompile(`(?m)^\s*import\s+(?:static\s+)?([A-Za-z_][\w.]*)(?:\.\*)?\s*;`)
	if language == "kotlin" {
		importRE = regexp.MustCompile(`(?m)^\s*import\s+([A-Za-z_][\w.]*)(?:\.\*)?`)
	}
	addRegexImports(analysis, text, []importPattern{{importRE, 1}})
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:public|private|protected|internal|static|final|open|override|\s)*\s*(?:fun|[A-Za-z_][\w<>\[\], ?]*\s+)([A-Za-z_]\w*)\s*\(`)) {
		name := match.Name
		if isControlKeyword(name) {
			continue
		}
		addFunction(analysis, name, match.Line, strings.Contains(match.Text, "public") || unicode.IsUpper([]rune(name)[0]))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:public|private|protected|internal|static|final|open|data|sealed|abstract|\s)*\s*(class|interface|enum|record|object|struct)\s+([A-Za-z_]\w*)`)) {
		addType(analysis, match.Groups[1], match.Groups[0], match.Line, strings.Contains(match.Text, "public") || unicode.IsUpper([]rune(match.Groups[1])[0]))
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*)\s*\(`), javaKeywords())
}

func extractPHP(analysis *extractor.StructuralAnalysis, text string) {
	addRegexImports(analysis, text, []importPattern{
		{regexp.MustCompile(`(?m)^\s*(?:include|require)(?:_once)?\s*\(?\s*["']([^"']+)["']`), 1},
		{regexp.MustCompile(`(?m)^\s*use\s+([A-Za-z_\\][\w\\]*)\s*;`), 1},
	})
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:public|private|protected|static|\s)*function\s+([A-Za-z_]\w*)\s*\(`)) {
		addFunction(analysis, match.Name, match.Line, !strings.HasPrefix(match.Name, "_"))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:abstract|final|\s)*(class|interface|trait|enum)\s+([A-Za-z_]\w*)`)) {
		addType(analysis, match.Groups[1], match.Groups[0], match.Line, true)
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*)\s*\(`), phpKeywords())
}

func extractC(language string, analysis *extractor.StructuralAnalysis, text string) {
	addRegexImports(analysis, text, []importPattern{{regexp.MustCompile(`(?m)^\s*#\s*include\s+["<]([^">]+)[">]`), 1}})
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:[A-Za-z_][\w\s\*\(\),]*\s+)+([A-Za-z_]\w*)\s*\([^;{}]*\)\s*\{`)) {
		name := match.Name
		if isControlKeyword(name) {
			continue
		}
		addFunction(analysis, name, match.Line, true)
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(?:typedef\s+)?(struct|enum|class)\s+([A-Za-z_]\w*)`)) {
		addType(analysis, match.Groups[1], match.Groups[0], match.Line, true)
	}
	if language == "cpp" {
		for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*class\s+([A-Za-z_]\w*)`)) {
			addType(analysis, match.Name, "class", match.Line, true)
		}
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*)\s*\(`), cKeywords())
}

func extractRuby(analysis *extractor.StructuralAnalysis, text string) {
	addRegexImports(analysis, text, []importPattern{{regexp.MustCompile(`(?m)^\s*require(?:_relative)?\s+["']([^"']+)["']`), 1}})
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*def\s+(?:self\.)?([A-Za-z_]\w*[!?=]?)`)) {
		addFunction(analysis, match.Name, match.Line, !strings.HasPrefix(match.Name, "_"))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(class|module)\s+([A-Z]\w*(?:::[A-Z]\w*)*)`)) {
		addType(analysis, match.Groups[1], match.Groups[0], match.Line, true)
	}
	addCallMatches(analysis, text, regexp.MustCompile(`\b([A-Za-z_]\w*[!?=]?)\s*(?:\(|$)`), rubyKeywords())
}
