package mongo

import (
	"testing"

	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/graph"
)

func TestSplitDeltaPayloadSinglePart(t *testing.T) {
	fileDeltas := []graph.FileDelta{
		{
			FilePath: "main.go",
			AddedNodes: []graph.Node{
				{ID: "sym:main.go#Main", Kind: graph.NodeKindSymbol, Name: "Main", Path: "main.go"},
			},
		},
	}

	parts, err := splitDeltaPayload(fileDeltas, 1<<20)
	if err != nil {
		t.Fatalf("splitDeltaPayload: %v", err)
	}
	if len(parts) != 1 {
		t.Fatalf("expected 1 part, got %d", len(parts))
	}
	if len(parts[0].FileDeltas) != 1 {
		t.Fatalf("unexpected part: %+v", parts[0])
	}
}

func TestSplitDeltaPayloadMultipleParts(t *testing.T) {
	fileDeltas := make([]graph.FileDelta, 0, 50)
	for i := 0; i < 50; i++ {
		fileDeltas = append(fileDeltas, graph.FileDelta{
			FilePath: "file_" + itoa(i) + ".go",
			AddedNodes: []graph.Node{
				{ID: "sym:file_" + itoa(i) + ".go#Fn", Kind: graph.NodeKindSymbol, Name: "Fn", Path: "file_" + itoa(i) + ".go"},
			},
			AddedEdges: []graph.Edge{
				{ID: "edge:" + itoa(i), Kind: graph.EdgeKindCalls, SourceID: "sym:a", TargetID: "sym:b"},
			},
		})
	}

	parts, err := splitDeltaPayload(fileDeltas, 512)
	if err != nil {
		t.Fatalf("splitDeltaPayload: %v", err)
	}
	if len(parts) < 2 {
		t.Fatalf("expected multiple parts with 512 byte limit, got %d", len(parts))
	}

	got := reassembleDeltaPayload(parts)
	if len(got) != len(fileDeltas) {
		t.Fatalf("reassembled file deltas: got %d want %d", len(got), len(fileDeltas))
	}
}

func TestSplitFileDeltaOversizedSingleFile(t *testing.T) {
	edges := make([]graph.Edge, 0, 200)
	for i := 0; i < 200; i++ {
		edges = append(edges, graph.Edge{
			ID:       "edge:big.go#calls@" + itoa(i),
			Kind:     graph.EdgeKindCalls,
			SourceID: "sym:big.go#fn",
			TargetID: "sym:pkg#Call",
		})
	}

	fd := graph.FileDelta{
		FilePath:   "big.go",
		AddedNodes: []graph.Node{{ID: "file:big.go", Kind: graph.NodeKindFile, Name: "big.go", Path: "big.go"}},
		AddedEdges: edges,
	}

	parts, err := splitFileDelta(fd, 512)
	if err != nil {
		t.Fatalf("splitFileDelta: %v", err)
	}
	if len(parts) < 2 {
		t.Fatalf("expected multiple shards, got %d", len(parts))
	}

	merged := mergeFileDeltasByPath(parts)
	if len(merged) != 1 {
		t.Fatalf("expected 1 merged file delta, got %d", len(merged))
	}
	if len(merged[0].AddedEdges) != len(edges) {
		t.Fatalf("edges: got %d want %d", len(merged[0].AddedEdges), len(edges))
	}
}

func TestReassembleDeltaPayloadMergesSamePath(t *testing.T) {
	parts := []deltaPayloadPart{
		{FileDeltas: []graph.FileDelta{{
			FilePath:   "big.go",
			AddedNodes: []graph.Node{{ID: "n1", Kind: graph.NodeKindSymbol, Name: "A", Path: "big.go"}},
			AddedEdges: []graph.Edge{{ID: "e1", Kind: graph.EdgeKindCalls, SourceID: "n1", TargetID: "n2"}},
		}}},
		{FileDeltas: []graph.FileDelta{{
			FilePath:     "big.go",
			AddedEdges:   []graph.Edge{{ID: "e2", Kind: graph.EdgeKindCalls, SourceID: "n2", TargetID: "n3"}},
			RemovedNodes: []graph.Node{{ID: "n0", Kind: graph.NodeKindSymbol, Name: "Old", Path: "big.go"}},
		}}},
	}

	got := reassembleDeltaPayload(parts)
	if len(got) != 1 {
		t.Fatalf("expected 1 file delta, got %d", len(got))
	}
	if len(got[0].AddedNodes) != 1 || len(got[0].RemovedNodes) != 1 || len(got[0].AddedEdges) != 2 {
		t.Fatalf("unexpected merge: %+v", got[0])
	}
}

func TestResolveDeltaChunked(t *testing.T) {
	if !resolveDeltaChunked(true, 0, 0) {
		t.Fatal("expected chunked when flag set")
	}
	if !resolveDeltaChunked(false, 3, 0) {
		t.Fatal("expected chunked when part_count set and inline empty")
	}
	if resolveDeltaChunked(false, 0, 5) {
		t.Fatal("expected not chunked for inline payload")
	}
}
