package context

import (
	stdctx "context"
	"strings"

	"github.com/blackwell-systems/knowing/internal/types"
)

// AutoGeneratedConcepts scans all symbols in the graph and generates
// equivalence classes from naming patterns. This provides repo-specific
// vocabulary without hand curation.
//
// Strategies:
//   - Handler pattern: "handleBlastRadius" -> concept "blast radius" -> target symbol
//   - Tool pattern: "blastRadiusTool" -> concept "blast radius tool" -> target symbol
//   - Package grouping: all symbols in "search/" -> concept phrases from package name
//   - Composite names: "IncrementalReindex" -> "incremental reindex" as searchable phrase
type AutoConceptGenerator struct {
	store types.GraphStore
}

// NewAutoConceptGenerator creates a generator backed by the given store.
func NewAutoConceptGenerator(store types.GraphStore) *AutoConceptGenerator {
	return &AutoConceptGenerator{store: store}
}

// Generate scans the graph and produces equivalence classes from symbol naming patterns.
func (g *AutoConceptGenerator) Generate(ctx stdctx.Context) []EquivalenceClass {
	nodes, err := g.store.NodesByName(ctx, "%")
	if err != nil || len(nodes) == 0 {
		return nil
	}

	var classes []EquivalenceClass

	// Generic words to filter from generated phrases.
	generic := map[string]bool{
		"handle": true, "new": true, "get": true, "set": true, "is": true,
		"has": true, "do": true, "run": true, "make": true, "init": true,
		"test": true, "mock": true, "err": true, "ctx": true, "ok": true,
		"the": true, "for": true, "with": true, "from": true, "and": true,
		"func": true, "type": true, "string": true, "int": true, "bool": true,
		"error": true, "nil": true, "true": true, "false": true,
	}

	seen := make(map[string]bool) // prevent duplicate concepts

	for _, node := range nodes {
		// Skip test/mock/vendor symbols.
		qn := node.QualifiedName
		if strings.Contains(qn, "mock") || strings.Contains(qn, "fake") ||
			strings.Contains(qn, "/vendor/") || strings.Contains(qn, "/dist/") ||
			strings.Contains(qn, "_test.") {
			continue
		}

		// Extract just the symbol name (after last dot).
		symName := qn
		if dot := strings.LastIndex(qn, "."); dot >= 0 {
			symName = qn[dot+1:]
		}

		// Skip very short or very common names.
		if len(symName) < 6 {
			continue
		}

		// Split CamelCase into words.
		words := splitCamelWords(symName)
		if len(words) < 2 {
			continue // single-word names don't generate useful phrases
		}

		// Filter generic words.
		var meaningful []string
		for _, w := range words {
			if !generic[strings.ToLower(w)] && len(w) >= 3 {
				meaningful = append(meaningful, w)
			}
		}
		if len(meaningful) < 2 {
			continue
		}

		// Create a natural-language phrase from the meaningful words.
		phrase := strings.ToLower(strings.Join(meaningful, " "))
		if seen[phrase] || len(phrase) < 6 {
			continue
		}
		seen[phrase] = true

		// Detect patterns and create targeted classes.
		class := EquivalenceClass{
			Concept:    "AUTO_" + strings.ToUpper(strings.ReplaceAll(phrase, " ", "_")),
			Phrases:    []string{phrase},
			Targets:    []string{symName},
			TargetType: "symbol",
			Weight:     0.6, // lower than seed (1.0) and universal (0.8)
			Source:     "generated",
		}

		// Handler pattern: "handleBlastRadius" -> also add without "handle" prefix.
		if strings.HasPrefix(symName, "handle") && len(meaningful) >= 2 {
			withoutHandle := strings.ToLower(strings.Join(meaningful, " "))
			if withoutHandle != phrase {
				class.Phrases = append(class.Phrases, withoutHandle)
			}
		}

		// Tool pattern: "blastRadiusTool" -> add phrase without "tool" suffix.
		if strings.HasSuffix(strings.ToLower(symName), "tool") && len(meaningful) >= 2 {
			withoutTool := make([]string, 0, len(meaningful))
			for _, w := range meaningful {
				if strings.ToLower(w) != "tool" {
					withoutTool = append(withoutTool, w)
				}
			}
			if len(withoutTool) > 0 {
				class.Phrases = append(class.Phrases, strings.ToLower(strings.Join(withoutTool, " ")))
			}
		}

		classes = append(classes, class)
	}

	// Cap total auto-generated classes to prevent bloat.
	if len(classes) > 200 {
		classes = classes[:200]
	}

	return classes
}

// splitCamelWords splits a CamelCase identifier into its word components.
// "BlastRadius" -> ["Blast", "Radius"]
// "handleGetCallChain" -> ["handle", "Get", "Call", "Chain"]
func splitCamelWords(s string) []string {
	var words []string
	current := strings.Builder{}
	for i, r := range s {
		if i > 0 && r >= 'A' && r <= 'Z' {
			prev := rune(s[i-1])
			if prev >= 'a' && prev <= 'z' {
				if current.Len() > 0 {
					words = append(words, current.String())
					current.Reset()
				}
			} else if prev >= 'A' && prev <= 'Z' && i+1 < len(s) && rune(s[i+1]) >= 'a' && rune(s[i+1]) <= 'z' {
				if current.Len() > 0 {
					words = append(words, current.String())
					current.Reset()
				}
			}
		}
		current.WriteRune(r)
	}
	if current.Len() > 0 {
		words = append(words, current.String())
	}
	return words
}
