package generic

import (
	"encoding/json"
	"regexp"
	"strings"

	"gopkg.in/yaml.v3"

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

func extractSQL(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?im)^\s*CREATE\s+(?:OR\s+REPLACE\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([A-Za-z_][\w."]*)`)) {
		analysis.SubNodes = append(analysis.SubNodes, subNode("table", cleanSQLName(match.Name), "SQL table "+cleanSQLName(match.Name), []string{"sql", "table"}, match.Line))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?im)^\s*CREATE\s+SCHEMA\s+(?:IF\s+NOT\s+EXISTS\s+)?([A-Za-z_][\w."]*)`)) {
		analysis.SubNodes = append(analysis.SubNodes, subNode("schema", cleanSQLName(match.Name), "SQL schema "+cleanSQLName(match.Name), []string{"sql", "schema"}, match.Line))
	}
}

func extractYAML(path string, analysis *extractor.StructuralAnalysis, text string) {
	if looksLikeOpenAPI(path, text) {
		extractOpenAPIYAML(analysis, []byte(text))
		return
	}
	if isKubernetesYAML(text) {
		extractKubernetesYAML(analysis, []byte(text))
	}
}

func extractJSON(path string, analysis *extractor.StructuralAnalysis, text string) {
	if !looksLikeOpenAPI(path, text) {
		return
	}
	var decoded map[string]any
	if err := json.Unmarshal([]byte(text), &decoded); err != nil {
		return
	}
	extractOpenAPIMap(analysis, decoded)
}

func extractMarkdown(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^(#{1,6})\s+(.+?)\s*$`)) {
		analysis.Exports = append(analysis.Exports, strings.TrimSpace(match.Groups[1]))
	}
}

func extractProto(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*import\s+["']([^"']+)["']\s*;`)) {
		analysis.Imports = append(analysis.Imports, extractor.Import{Path: match.Name})
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(message|enum)\s+([A-Za-z_]\w*)`)) {
		analysis.SubNodes = append(analysis.SubNodes, subNode("schema", match.Groups[1], "Protobuf "+match.Groups[0]+" "+match.Groups[1], []string{"protobuf", match.Groups[0]}, match.Line))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*service\s+([A-Za-z_]\w*)`)) {
		analysis.SubNodes = append(analysis.SubNodes, subNode("service", match.Name, "Protobuf service "+match.Name, []string{"protobuf", "service"}, match.Line))
	}
}

func extractGraphQL(analysis *extractor.StructuralAnalysis, text string) {
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(type|interface|enum|input|scalar|union)\s+([A-Za-z_]\w*)`)) {
		nodeType := "schema"
		if match.Groups[1] == "Query" || match.Groups[1] == "Mutation" || match.Groups[1] == "Subscription" {
			nodeType = "endpoint"
		}
		analysis.SubNodes = append(analysis.SubNodes, subNode(nodeType, match.Groups[1], "GraphQL "+match.Groups[0]+" "+match.Groups[1], []string{"graphql", match.Groups[0]}, match.Line))
	}
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?m)^\s*(query|mutation|subscription)\s+([A-Za-z_]\w*)`)) {
		analysis.SubNodes = append(analysis.SubNodes, subNode("endpoint", match.Groups[1], "GraphQL "+match.Groups[0]+" "+match.Groups[1], []string{"graphql", match.Groups[0]}, match.Line))
	}
}

func extractDockerfile(analysis *extractor.StructuralAnalysis, text string) {
	stage := "image"
	for _, match := range findLineMatches(text, regexp.MustCompile(`(?im)^\s*FROM\s+\S+(?:\s+AS\s+([A-Za-z0-9_.-]+))?`)) {
		if match.Name != "" {
			stage = match.Name
		}
		analysis.SubNodes = append(analysis.SubNodes, subNode("service", stage, "Docker build stage "+stage, []string{"docker", "service"}, match.Line))
	}
}

func extractKubernetesYAML(analysis *extractor.StructuralAnalysis, data []byte) {
	parts := strings.Split(string(data), "\n---")
	for _, part := range parts {
		var doc map[string]any
		if yaml.Unmarshal([]byte(part), &doc) != nil {
			continue
		}
		kind := stringValue(doc["kind"])
		if kind == "" {
			continue
		}
		name := metadataName(doc)
		if name == "" {
			name = strings.ToLower(kind)
		}
		nodeType := "resource"
		switch strings.ToLower(kind) {
		case "deployment", "statefulset", "daemonset", "service", "pod", "job", "cronjob":
			nodeType = "service"
		}
		analysis.SubNodes = append(analysis.SubNodes, extractor.SubNode{Type: nodeType, Name: name, Summary: "Kubernetes " + kind + " " + name, Tags: []string{"kubernetes", strings.ToLower(kind)}, LineRange: [2]int{1, 1}})
	}
}

func extractOpenAPIYAML(analysis *extractor.StructuralAnalysis, data []byte) {
	var decoded map[string]any
	if yaml.Unmarshal(data, &decoded) != nil {
		return
	}
	extractOpenAPIMap(analysis, decoded)
}

func extractOpenAPIMap(analysis *extractor.StructuralAnalysis, decoded map[string]any) {
	if !hasOpenAPIMarker(decoded) {
		return
	}
	if paths, ok := decoded["paths"].(map[string]any); ok {
		for path, rawMethods := range paths {
			methods, _ := rawMethods.(map[string]any)
			if len(methods) == 0 {
				analysis.SubNodes = append(analysis.SubNodes, subNode("endpoint", path, "OpenAPI endpoint "+path, []string{"openapi", "endpoint"}, 1))
				continue
			}
			for method := range methods {
				lower := strings.ToLower(method)
				if !httpMethods()[lower] {
					continue
				}
				name := strings.ToUpper(lower) + " " + path
				analysis.SubNodes = append(analysis.SubNodes, subNode("endpoint", name, "OpenAPI endpoint "+name, []string{"openapi", "endpoint"}, 1))
			}
		}
	}
	if components, ok := decoded["components"].(map[string]any); ok {
		if schemas, ok := components["schemas"].(map[string]any); ok {
			for name := range schemas {
				analysis.SubNodes = append(analysis.SubNodes, subNode("schema", name, "OpenAPI schema "+name, []string{"openapi", "schema"}, 1))
			}
		}
	}
}
