package contracts

import (
	"testing"
)

// TestExtractGoShape_NestedAnonymousStruct guards the regression
// where the line-based extractor hoisted fields of an inline
// anonymous struct into the outer struct's field list — producing
// duplicate names when the parent and substruct shared field names
// (e.g. dashboardSnapshot's outer `Repos` / `Caveats` clashing with
// the embedded Stats.Repos / Stats.Caveats fields). React then
// rendered two children with the same key and warned in the
// dashboard.
//
// Fix: track brace depth and only emit fields at depth 0 (direct
// members of the outer struct). Nested-struct fields are dropped
// rather than mis-attributed.
func TestExtractGoShape_NestedAnonymousStruct(t *testing.T) {
	src := []byte(`type dashboardSnapshot struct {
	Stats struct {
		TotalNodes int    ` + "`json:\"total_nodes\"`" + `
		TotalEdges int    ` + "`json:\"total_edges\"`" + `
		Repos      int    ` + "`json:\"repos\"`" + `
		Caveats    int    ` + "`json:\"caveats\"`" + `
		Version    string ` + "`json:\"version\"`" + `
	} ` + "`json:\"stats\"`" + `
	Kinds     []kvEntry      ` + "`json:\"kinds\"`" + `
	Languages []kvEntry      ` + "`json:\"languages\"`" + `
	Repos     []repoEntry    ` + "`json:\"repos\"`" + `
	Activity  []changeEvent  ` + "`json:\"activity\"`" + `
	Caveats   []caveatEntry  ` + "`json:\"caveats\"`" + `
	Processes []processEntry ` + "`json:\"processes\"`" + `
}
`)
	shape := extractGoShape(src, 1, 16)
	if shape == nil {
		t.Fatal("expected non-nil shape")
	}
	seen := map[string]int{}
	for _, f := range shape.Fields {
		seen[f.Name]++
	}
	for name, count := range seen {
		if count > 1 {
			t.Errorf("field %q appears %d times — duplicate names hoist nested struct into parent", name, count)
		}
	}
	// Each top-level field must show up exactly once. We don't assert
	// the parent `stats` is present (the regex doesn't match the
	// `Stats struct {` line — that's an acceptable under-report; the
	// alternative — over-reporting with duplicates — broke the React
	// tree).
	wantTopLevel := []string{"kinds", "languages", "repos", "activity", "caveats", "processes"}
	for _, name := range wantTopLevel {
		if seen[name] != 1 {
			t.Errorf("top-level field %q expected once, got %d", name, seen[name])
		}
	}
	// Inner Stats fields MUST NOT be hoisted (they collide with the
	// outer ones). Specifically `total_nodes` is unique to the inner
	// struct, so its presence proves the bug recurred.
	if seen["total_nodes"] > 0 {
		t.Errorf("inner Stats.TotalNodes leaked into outer shape (hoisting regression)")
	}
}
