package commitintel_test

import (
	"strings"
	"testing"

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/events"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/graph"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/service/commitintel"
)

func TestDeltaImpactAnalyzerEmptyDelta(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_empty",
		"ad/example",
		"base0000000001",
		"target00000002",
		"delta_empty",
		"file:///tmp/empty.json",
		"v1",
	)
	event.AddedNodes = 0
	event.RemovedNodes = 0
	event.ChangedEdges = 0

	delta := graph.Delta{SchemaVersion: graph.DeltaSchemaVersionV1}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_empty")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if len(analysis.ImpactedSymbols) != 0 {
		t.Fatalf("impacted_symbols: %#v", analysis.ImpactedSymbols)
	}
	if analysis.Summary == "" {
		t.Fatal("expected short summary for empty delta")
	}
	if !strings.Contains(analysis.Summary, "limited delta detail") {
		t.Fatalf("summary: %q", analysis.Summary)
	}
	if analysis.CommitSHA != event.TargetCommitSHA {
		t.Fatalf("commit_sha: got %q want %q", analysis.CommitSHA, event.TargetCommitSHA)
	}
}

func TestDeltaImpactAnalyzerSingleFileTwoSymbolsAdded(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_single",
		"ad/example",
		"base11111111",
		"target22222222",
		"delta_single",
		"file:///tmp/single.json",
		"v1",
	)
	event.AddedNodes = 2
	event.RemovedNodes = 0

	delta := graph.Delta{
		SchemaVersion: graph.DeltaSchemaVersionV1,
		ChangedFiles:  []string{"pkg/handler.go"},
		FileDeltas: []graph.FileDelta{
			{
				FilePath: "pkg/handler.go",
				AddedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "Handle"},
					{Kind: graph.NodeKindSymbol, Name: "Validate"},
				},
			},
		},
	}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_single")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if len(analysis.ImpactedSymbols) != 2 {
		t.Fatalf("impacted_symbols: %#v", analysis.ImpactedSymbols)
	}
	want := map[string]bool{"Handle": true, "Validate": true}
	for _, name := range analysis.ImpactedSymbols {
		if !want[name] {
			t.Fatalf("unexpected symbol %q", name)
		}
	}
	if len(analysis.ChangedFiles) != 1 || analysis.ChangedFiles[0] != "pkg/handler.go" {
		t.Fatalf("changed_files: %#v", analysis.ChangedFiles)
	}
}

func TestDeltaImpactAnalyzerMultiFile(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_multi",
		"ad/example",
		"base1111",
		"target2222",
		"delta_multi",
		"file:///tmp/multi.json",
		"v1",
	)
	event.AddedNodes = 2
	event.RemovedNodes = 1

	delta := graph.Delta{
		SchemaVersion: graph.DeltaSchemaVersionV1,
		ChangedFiles:  []string{"a.go", "b.go"},
		FileDeltas: []graph.FileDelta{
			{
				FilePath: "a.go",
				AddedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "Foo"},
				},
			},
			{
				FilePath: "b.go",
				RemovedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "Bar"},
				},
			},
		},
	}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_multi")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if len(analysis.ChangedFiles) != 2 {
		t.Fatalf("changed_files: %#v", analysis.ChangedFiles)
	}
	if !strings.Contains(analysis.Summary, "across 2 files") {
		t.Fatalf("summary should mention file count: %q", analysis.Summary)
	}
}

func TestDeltaImpactAnalyzerChangedFilesFromFileDeltas(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_paths",
		"ad/example",
		"base",
		"target",
		"delta_paths",
		"file:///tmp/paths.json",
		"v1",
	)

	delta := graph.Delta{
		SchemaVersion: graph.DeltaSchemaVersionV1,
		FileDeltas: []graph.FileDelta{
			{FilePath: "x.go"},
			{FilePath: "y.go"},
		},
	}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_paths")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if len(analysis.ChangedFiles) != 2 {
		t.Fatalf("changed_files: %#v", analysis.ChangedFiles)
	}
}

func TestDeltaImpactAnalyzerSummaryTruncation(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_long",
		"ad/example",
		"base",
		"target",
		"delta_long",
		"file:///tmp/long.json",
		"v1",
	)
	event.AddedNodes = 1000
	event.RemovedNodes = 1000

	longFiles := make([]string, 200)
	for i := range longFiles {
		longFiles[i] = strings.Repeat("x", 50) + ".go"
	}

	delta := graph.Delta{
		SchemaVersion: graph.DeltaSchemaVersionV1,
		ChangedFiles:  longFiles,
		FileDeltas:    []graph.FileDelta{{FilePath: "main.go"}},
	}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_long")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if len(analysis.Summary) > 500 {
		t.Fatalf("summary length %d exceeds cap", len(analysis.Summary))
	}
}

func TestDeltaImpactAnalyzerDeterministic(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_det",
		"ad/example",
		"baseaaaaaaaa",
		"targetbbbbbbbb",
		"delta_det",
		"file:///tmp/det.json",
		"v1",
	)
	event.AddedNodes = 1
	event.RemovedNodes = 1

	delta := graph.Delta{
		SchemaVersion:   graph.DeltaSchemaVersionV1,
		ChangedFiles:    []string{"main.go"},
		FileDeltas: []graph.FileDelta{
			{
				FilePath: "main.go",
				AddedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "Run"},
				},
				RemovedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "Stop"},
				},
			},
		},
	}

	first, err := analyzer.AnalyzeDelta(delta, event, "analysis_det")
	if err != nil {
		t.Fatalf("first AnalyzeDelta: %v", err)
	}
	second, err := analyzer.AnalyzeDelta(delta, event, "analysis_det")
	if err != nil {
		t.Fatalf("second AnalyzeDelta: %v", err)
	}

	if first.Summary != second.Summary {
		t.Fatalf("summary not deterministic: %q vs %q", first.Summary, second.Summary)
	}
	if strings.Join(first.ImpactedSymbols, ",") != strings.Join(second.ImpactedSymbols, ",") {
		t.Fatalf("symbols not deterministic: %#v vs %#v", first.ImpactedSymbols, second.ImpactedSymbols)
	}
}

func TestDeltaImpactAnalyzerPrefersEventCounts(t *testing.T) {
	analyzer := commitintel.NewDeltaImpactAnalyzer()
	event := events.NewGraphDeltaReadyEvent(
		"evt_counts",
		"ad/example",
		"base",
		"target",
		"delta_counts",
		"file:///tmp/counts.json",
		"v1",
	)
	event.AddedNodes = 5
	event.RemovedNodes = 3
	event.ChangedEdges = 2

	delta := graph.Delta{
		SchemaVersion: graph.DeltaSchemaVersionV1,
		ChangedFiles:  []string{"main.go"},
		FileDeltas: []graph.FileDelta{
			{
				FilePath: "main.go",
				AddedNodes: []graph.Node{
					{Kind: graph.NodeKindSymbol, Name: "OnlyOne"},
				},
			},
		},
	}

	analysis, err := analyzer.AnalyzeDelta(delta, event, "analysis_counts")
	if err != nil {
		t.Fatalf("AnalyzeDelta: %v", err)
	}
	if !strings.Contains(analysis.Summary, "5 symbols added, 3 removed") {
		t.Fatalf("summary should use event counts: %q", analysis.Summary)
	}
}
