package mongo_test

import (
	"context"
	"fmt"
	"testing"

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

func TestStoreSaveAndLoadArtifact(t *testing.T) {
	store, cleanup := setupMongoStore(t)
	defer cleanup()

	artifact := graph.Artifact{
		SchemaVersion: graph.SchemaVersionV1,
		RepoID:        "ad/example",
		SnapshotID:    "snap_mongo",
		CommitSHA:     "abc123",
		Nodes: []graph.Node{
			{ID: "file_main", Kind: graph.NodeKindFile, Name: "main.go", Path: "main.go", Language: "go"},
		},
	}

	id, uri, err := store.SaveArtifact(context.Background(), artifact)
	if err != nil {
		t.Fatalf("SaveArtifact: %v", err)
	}
	if uri == "" || id == "" {
		t.Fatal("expected artifact id and mongo uri")
	}

	loaded, err := store.GetArtifact(context.Background(), id)
	if err != nil {
		t.Fatalf("GetArtifact: %v", err)
	}
	if loaded.RepoID != artifact.RepoID || len(loaded.Nodes) != 1 {
		t.Fatalf("unexpected loaded artifact: %+v", loaded)
	}

	metadata, err := store.GetArtifactMetadata(context.Background(), id)
	if err != nil {
		t.Fatalf("GetArtifactMetadata: %v", err)
	}
	if metadata.FilePath != "main.go" {
		t.Fatalf("file_path: got %q", metadata.FilePath)
	}
}

func TestStoreSaveAndLoadChunkedArtifact(t *testing.T) {
	store, cleanup := setupMongoStore(t)
	defer cleanup()

	nodes := []graph.Node{
		{ID: "file:big.go", Kind: graph.NodeKindFile, Name: "big.go", Path: "big.go", Language: "go"},
	}
	edges := make([]graph.Edge, 0, 300)
	for i := 0; i < 300; i++ {
		edges = append(edges, graph.Edge{
			ID:       fmt.Sprintf("edge:big.go#calls@%d#sym:big.go#fn#sym:pkg#Call", i),
			Kind:     graph.EdgeKindCalls,
			SourceID: "sym:big.go#fn",
			TargetID: "sym:pkg#Call",
		})
	}

	artifact := graph.Artifact{
		SchemaVersion: graph.SchemaVersionV1,
		RepoID:        "ad/example",
		SnapshotID:    "snap_chunked",
		CommitSHA:     "abc123",
		Nodes:         nodes,
		Edges:         edges,
	}

	id, _, err := store.SaveArtifact(context.Background(), artifact)
	if err != nil {
		t.Fatalf("SaveArtifact chunked: %v", err)
	}

	loaded, err := store.GetArtifact(context.Background(), id)
	if err != nil {
		t.Fatalf("GetArtifact chunked: %v", err)
	}
	if len(loaded.Nodes) != len(nodes) {
		t.Fatalf("nodes: got %d want %d", len(loaded.Nodes), len(nodes))
	}
	if len(loaded.Edges) != len(edges) {
		t.Fatalf("edges: got %d want %d", len(loaded.Edges), len(edges))
	}

	metadata, err := store.GetArtifactMetadata(context.Background(), id)
	if err != nil {
		t.Fatalf("GetArtifactMetadata chunked: %v", err)
	}
	if metadata.NodeCount != len(nodes) || metadata.EdgeCount != len(edges) {
		t.Fatalf("metadata counts: nodes=%d edges=%d", metadata.NodeCount, metadata.EdgeCount)
	}
}

func setupMongoStore(t *testing.T) (*mongostore.Store, func()) {
	t.Helper()

	cfg := config.MongoConfig{
		URI:                  "mongodb://localhost:27018/adpilot_code_parser_test",
		Database:             "adpilot_code_parser_test",
		DocsDatabase:         "repo_docs",
		IndexingRunsDatabase: "adpilot_repo_sync",
		IndexingRunsEnabled:  false,
		ArtifactMaxPartBytes: 512,
	}

	store, err := mongostore.New(cfg)
	if err != nil {
		t.Skipf("mongo not available: %v", err)
	}

	ctx := context.Background()
	if err := store.Ping(ctx); err != nil {
		_ = store.Close(ctx)
		t.Skipf("mongo not reachable: %v", err)
	}
	if err := store.EnsureIndexes(ctx); err != nil {
		_ = store.Close(ctx)
		t.Fatalf("EnsureIndexes: %v", err)
	}

	cleanup := func() {
		_ = store.Close(context.Background())
	}
	return store, cleanup
}
