package mongo_test

import (
	"context"
	"fmt"
	"testing"

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

func TestStoreSaveAndLoadChunkedDelta(t *testing.T) {
	mongoStore, cleanup := setupMongoStore(t)
	defer cleanup()

	ctx := context.Background()
	fileDeltas := make([]graph.FileDelta, 0, 20)
	for i := 0; i < 20; i++ {
		edges := make([]graph.Edge, 0, 40)
		for j := 0; j < 40; j++ {
			edges = append(edges, graph.Edge{
				ID:       fmt.Sprintf("edge:file%d.go#calls@%d#sym:file%d.go#fn#sym:pkg#Call", i, j, i),
				Kind:     graph.EdgeKindCalls,
				SourceID: fmt.Sprintf("sym:file%d.go#fn", i),
				TargetID: "sym:pkg#Call",
			})
		}
		fileDeltas = append(fileDeltas, graph.FileDelta{
			FilePath:         fmt.Sprintf("internal/file%d.go", i),
			BaseArtifactID:   fmt.Sprintf("art_base_%d", i),
			TargetArtifactID: fmt.Sprintf("art_target_%d", i),
			AddedNodes: []graph.Node{
				{ID: fmt.Sprintf("sym:file%d.go#fn", i), Kind: graph.NodeKindSymbol, Name: "Run", Path: fmt.Sprintf("internal/file%d.go", i)},
			},
			AddedEdges: edges,
		})
	}

	delta := graph.Delta{
		SchemaVersion:   graph.DeltaSchemaVersionV1,
		RepoID:          "ad/example",
		SnapshotID:      "snap_chunked_delta",
		BaseCommitSHA:   "base1234567890abcd",
		TargetCommitSHA: "target1234567890ab",
		ChangedFiles:    []string{"internal/file0.go"},
		FileDeltas:      fileDeltas,
	}

	id, uri, err := mongoStore.SaveDelta(ctx, delta)
	if err != nil {
		t.Fatalf("SaveDelta chunked: %v", err)
	}
	if uri != store.MongoURI("graph_deltas", id) {
		t.Fatalf("unexpected uri: %s", uri)
	}

	loaded, err := mongoStore.LoadDeltaByID(ctx, id)
	if err != nil {
		t.Fatalf("LoadDeltaByID: %v", err)
	}
	if len(loaded.FileDeltas) != len(fileDeltas) {
		t.Fatalf("file deltas: got %d want %d", len(loaded.FileDeltas), len(fileDeltas))
	}

	wantAdded, wantRemoved, wantEdges := 0, 0, 0
	for _, fd := range fileDeltas {
		wantAdded += len(fd.AddedNodes)
		wantRemoved += len(fd.RemovedNodes)
		wantEdges += len(fd.AddedEdges) + len(fd.RemovedEdges)
	}
	gotAdded, gotRemoved, gotEdges := 0, 0, 0
	for _, fd := range loaded.FileDeltas {
		gotAdded += len(fd.AddedNodes)
		gotRemoved += len(fd.RemovedNodes)
		gotEdges += len(fd.AddedEdges) + len(fd.RemovedEdges)
	}
	if gotAdded != wantAdded || gotRemoved != wantRemoved || gotEdges != wantEdges {
		t.Fatalf("counts mismatch: added=%d/%d removed=%d/%d edges=%d/%d",
			gotAdded, wantAdded, gotRemoved, wantRemoved, gotEdges, wantEdges)
	}
}

func TestStoreSaveAndLoadInlineDelta(t *testing.T) {
	mongoStore, cleanup := setupMongoStore(t)
	defer cleanup()

	ctx := context.Background()
	delta := graph.Delta{
		SchemaVersion:   graph.DeltaSchemaVersionV1,
		RepoID:          "ad/example",
		SnapshotID:      "snap_inline_delta",
		BaseCommitSHA:   "base9876543210abcd",
		TargetCommitSHA: "target9876543210ab",
		ChangedFiles:    []string{"main.go"},
		FileDeltas: []graph.FileDelta{
			{
				FilePath: "main.go",
				AddedNodes: []graph.Node{
					{ID: "sym:main.go#main", Kind: graph.NodeKindSymbol, Name: "main", Path: "main.go"},
				},
			},
		},
	}

	id, _, err := mongoStore.SaveDelta(ctx, delta)
	if err != nil {
		t.Fatalf("SaveDelta inline: %v", err)
	}

	loaded, err := mongoStore.LoadDeltaByID(ctx, id)
	if err != nil {
		t.Fatalf("LoadDeltaByID: %v", err)
	}
	if len(loaded.FileDeltas) != 1 || loaded.FileDeltas[0].FilePath != "main.go" {
		t.Fatalf("unexpected loaded delta: %+v", loaded.FileDeltas)
	}

	expectedID := store.BuildDeltaID(delta.RepoID, delta.BaseCommitSHA, delta.TargetCommitSHA)
	if id != expectedID {
		t.Fatalf("delta id: got %q want %q", id, expectedID)
	}
}
