package claudemd

import (
	"fmt"
	"sort"
	"strings"

	"github.com/zzet/gortex/internal/graph"
	"github.com/zzet/gortex/internal/query"
)

// Generate produces a CLAUDE.md block describing the indexed codebase and how to use Gortex tools.
func Generate(engine *query.Engine, _ int) string {

	stats := engine.Stats()
	var b strings.Builder

	b.WriteString("## Codebase Overview (generated by Gortex)\n\n")

	// Language breakdown.
	langs := sortedKeys(stats.ByLanguage)
	if len(langs) > 0 {
		primary := langs[0]
		for _, l := range langs {
			if stats.ByLanguage[l] > stats.ByLanguage[primary] {
				primary = l
			}
		}
		langParts := make([]string, 0, len(langs))
		for _, l := range langs {
			if l == primary {
				langParts = append([]string{fmt.Sprintf("%s (primary)", l)}, langParts...)
			} else {
				langParts = append(langParts, l)
			}
		}
		fmt.Fprintf(&b, "- **Languages:** %s\n", strings.Join(langParts, ", "))
	}

	// Entry points (main functions).
	entryPoints := engine.FindSymbols("main", graph.KindFunction)
	if len(entryPoints) > 0 {
		paths := make([]string, 0, len(entryPoints))
		for _, ep := range entryPoints {
			paths = append(paths, fmt.Sprintf("`%s`", ep.FilePath))
		}
		fmt.Fprintf(&b, "- **Entry points:** %s\n", strings.Join(paths, ", "))
	}

	// Key symbols (most referenced).
	topSymbols := findMostReferenced(engine, 10)
	if len(topSymbols) > 0 {
		parts := make([]string, 0, len(topSymbols))
		for _, s := range topSymbols {
			parts = append(parts, fmt.Sprintf("`%s` (%d usages)", s.name, s.count))
		}
		fmt.Fprintf(&b, "- **Most-referenced symbols:** %s\n", strings.Join(parts, ", "))
	}

	fmt.Fprintf(&b, "- **Graph size:** %d nodes, %d edges\n", stats.TotalNodes, stats.TotalEdges)

	// Kind breakdown.
	if len(stats.ByKind) > 0 {
		parts := make([]string, 0, len(stats.ByKind))
		for _, k := range sortedKeys(stats.ByKind) {
			parts = append(parts, fmt.Sprintf("%d %ss", stats.ByKind[k], k))
		}
		fmt.Fprintf(&b, "- **Breakdown:** %s\n", strings.Join(parts, ", "))
	}

	b.WriteString("\n## MANDATORY: Use Gortex MCP tools instead of Read/Grep/Glob\n\n")
	b.WriteString("Gortex is running as an MCP server. You **MUST** prefer graph queries over file reads on every task in this repo — `search_symbols`, `find_usages`, `get_symbol_source`, `get_editing_context`, `smart_context`, `edit_symbol` / `edit_file` / `rename_symbol` / `batch_edit`. PreToolUse hooks deny `Read` / `Grep` / `Glob` against indexed source; the deny message names the right tool. The full per-tool catalog loads via `tools/list` — not restated here.\n\n")

	b.WriteString("### Calibration: the graph narrows scope, source confirms behavior\n\n")
	b.WriteString("The mandate above stands — but graph queries *narrow scope*, they do not *replace reading the implementation*. The graph tells you **where** the logic lives and **what** connects to it; the source tells you **how** it behaves. For the symbol you are about to change or depend on, read its full body with `get_symbol_source` — do not act on a one-line summary alone.\n\n")
	b.WriteString("Be especially deliberate with **behavior-critical code** — database migrations, retry / fallback / error-recovery paths, compatibility shims, concurrency-sensitive sections, and the tests that pin them. For these, call `get_symbol_source` and read the real implementation; never pass `compress_bodies:true`, which elides exactly the branches that carry the risk. Reserve compressed bodies and graph summaries for breadth (surveying many symbols); use full source for the few you are about to commit to.\n\n")
	b.WriteString("## Required workflow (every task on this repo)\n\n")
	b.WriteString("These are not suggestions — run each step at the trigger.\n\n")
	b.WriteString("1. **Always call** `graph_stats` first to confirm the daemon is up and orient (check `per_repo` in multi-repo mode).\n")
	b.WriteString("2. If `total_nodes` is 0, **call** `index_repository` with `\".\"` before anything else.\n")
	b.WriteString("3. In multi-repo mode, **call** `get_active_project` to check scope; use `set_active_project` to switch.\n")
	b.WriteString("4. For every new task, **call** `smart_context` with the task description before reading any file.\n")
	b.WriteString("5. Before editing a file, **call** `get_editing_context` on it first.\n")
	b.WriteString("6. Before changing any function signature, **call** `verify_change` to catch broken callers and interface implementors (cross-repo).\n")
	b.WriteString("7. For any refactor, **call** `get_edit_plan` then `batch_edit` to apply atomically.\n")
	b.WriteString("8. After every edit, **call** `check_guards` then `get_test_targets`.\n")

	return b.String()
}

type symbolRef struct {
	name  string
	count int
}

func findMostReferenced(engine *query.Engine, limit int) []symbolRef {
	allNodes := engine.AllNodes()
	relevantKinds := map[graph.EdgeKind]bool{
		graph.EdgeCalls:        true,
		graph.EdgeReferences:   true,
		graph.EdgeInstantiates: true,
		graph.EdgeImplements:   true,
	}

	var refs []symbolRef
	for _, n := range allNodes {
		if n.Kind == graph.KindFile || n.Kind == graph.KindImport {
			continue
		}
		count := 0
		for _, e := range engine.GetInEdges(n.ID) {
			if relevantKinds[e.Kind] {
				count++
			}
		}
		if count > 0 {
			refs = append(refs, symbolRef{name: n.Name, count: count})
		}
	}

	sort.Slice(refs, func(i, j int) bool {
		return refs[i].count > refs[j].count
	})

	if len(refs) > limit {
		refs = refs[:limit]
	}
	return refs
}

func sortedKeys(m map[string]int) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}
