package layers

import (
	"path/filepath"
	"strings"
	"unicode"

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

type Options struct {
	ProjectName string
}

// Generate is the deterministic layer detector fallback. Its pattern matching
// and ordering port Understand Anything's detectLayers. The only schema
// adaptation is using AgentTeam Insight's file-level node set while excluding child
// artifacts already represented by a contains edge.
func Generate(graph schema.KnowledgeGraph, _ Options) []schema.Layer {
	layers := make([]schema.Layer, 0)
	layerIndex := map[string]int{}
	contained := containedNodeIDs(graph.Edges)
	corePathless := make([]string, 0)

	for _, node := range graph.Nodes {
		if node.ID == "" || !schema.IsFileLevelNode(node) {
			continue
		}
		if _, ok := contained[node.ID]; ok {
			continue
		}
		if node.FilePath == "" {
			corePathless = append(corePathless, node.ID)
			continue
		}
		name, description := matchFileToLayer(filepath.ToSlash(node.FilePath))
		layers = appendLayerNode(layers, layerIndex, name, description, node.ID)
	}
	for _, id := range corePathless {
		layers = appendLayerNode(layers, layerIndex, "Core", "Core application files", id)
	}
	return layers
}

func appendLayerNode(layers []schema.Layer, index map[string]int, name, description, nodeID string) []schema.Layer {
	i, ok := index[name]
	if !ok {
		i = len(layers)
		index[name] = i
		layers = append(layers, schema.Layer{
			ID:          "layer:" + slug(name),
			Name:        name,
			Description: description,
			NodeIDs:     []string{},
		})
	}
	layers[i].NodeIDs = append(layers[i].NodeIDs, nodeID)
	return layers
}

func containedNodeIDs(edges []schema.GraphEdge) map[string]struct{} {
	contained := map[string]struct{}{}
	for _, edge := range edges {
		if edge.Type == "contains" && edge.Target != "" {
			contained[edge.Target] = struct{}{}
		}
	}
	return contained
}

type layerPattern struct {
	patterns    []string
	name        string
	description string
}

// Order matters: the first matching pattern owns the node. This table is a
// direct port of Understand Anything layer-detector.ts::LAYER_PATTERNS at
// revision 6ae71878.
var layerPatterns = []layerPattern{
	{patterns: []string{"routes", "controller", "handler", "endpoint", "api"}, name: "API Layer", description: "HTTP endpoints, route handlers, and API controllers"},
	{patterns: []string{"service", "usecase", "use-case", "business"}, name: "Service Layer", description: "Business logic and application services"},
	{patterns: []string{"model", "entity", "schema", "database", "db", "migration", "repository", "repo"}, name: "Data Layer", description: "Data models, database access, and persistence"},
	{patterns: []string{"component", "view", "page", "screen", "layout", "widget", "ui"}, name: "UI Layer", description: "User interface components and views"},
	{patterns: []string{"middleware", "interceptor", "guard", "filter", "pipe"}, name: "Middleware Layer", description: "Request/response middleware and interceptors"},
	{patterns: []string{"client", "integration", "external", "sdk", "vendor", "adapter"}, name: "External Services", description: "External service integrations, SDKs, and third-party adapters"},
	{patterns: []string{"worker", "job", "queue", "cron", "consumer", "processor", "scheduler", "background"}, name: "Background Tasks", description: "Background workers, job processors, and scheduled tasks"},
	{patterns: []string{"util", "helper", "lib", "common", "shared"}, name: "Utility Layer", description: "Shared utilities, helpers, and common libraries"},
	{patterns: []string{"test", "spec", "__test__", "__spec__", "__tests__", "__specs__"}, name: "Test Layer", description: "Test files and test utilities"},
	{patterns: []string{"config", "setting", "env"}, name: "Configuration Layer", description: "Application configuration and environment settings"},
}

func matchFileToLayer(path string) (string, string) {
	segments := strings.Split(strings.ToLower(strings.ReplaceAll(path, `\`, "/")), "/")
	for _, pattern := range layerPatterns {
		for _, segment := range segments {
			for _, candidate := range pattern.patterns {
				if segment == candidate || segment == candidate+"s" {
					return pattern.name, pattern.description
				}
			}
		}
	}
	return "Core", "Core application files"
}

func slug(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	var b strings.Builder
	lastDash := false
	for _, r := range value {
		switch {
		case unicode.IsLetter(r), unicode.IsDigit(r):
			b.WriteRune(r)
			lastDash = false
		default:
			if !lastDash {
				b.WriteByte('-')
				lastDash = true
			}
		}
	}
	return strings.Trim(b.String(), "-")
}
