//go:build !vectors

package memorysearch

import (
	"context"
	"fmt"
	"testing"
)

// BenchmarkSearchWarm measures the user-facing Search path over an already
// built generation. Setup and index construction are outside the timed agentteam.
func BenchmarkSearchWarm(b *testing.B) {
	h := newSearchHarness(b, benchmarkCorpus(200))
	ctx := context.Background()
	requests := []SearchRequest{
		{Query: "#2731", Limit: 5},
		{Query: "重号迁移", Limit: 5},
		{Query: "identifier extraction", Limit: 5},
		{Query: "user communication preference", Limit: 5},
	}
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		response, err := Search(ctx, h.src, requests[i%len(requests)])
		if err != nil {
			b.Fatal(err)
		}
		if response.State != StateReady {
			b.Fatalf("state = %s", response.State)
		}
	}
}

// BenchmarkBuildGeneration measures a complete Scan+Chunk+Bleve generation
// build over a representative 200-file (~1 MiB) durable Memory corpus.
func BenchmarkBuildGeneration(b *testing.B) {
	files := benchmarkCorpus(200)
	h := newBuildTestHarness(b, files)
	ctx := context.Background()
	snapshot, chunks := h.scanAndChunk(ctx, b)
	var totalBytes int64
	for _, file := range snapshot.Files {
		totalBytes += file.Size
	}
	b.ReportAllocs()
	b.SetBytes(totalBytes)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		generation, err := Build(ctx, BuildOptions{
			Binding:  h.binding,
			Resolver: h.resolver,
			Snapshot: snapshot,
			Chunks:   chunks,
		})
		if err != nil {
			b.Fatal(err)
		}
		if err := generation.Close(); err != nil {
			b.Fatal(err)
		}
	}
}

func benchmarkCorpus(count int) map[string]string {
	files := make(map[string]string, count+1)
	files["MEMORY.md"] = "# Memory\n\nDurable benchmark corpus index.\n"
	for i := 0; i < count; i++ {
		files[fmt.Sprintf("knowledge/concepts/concept-%03d.md", i)] = fmt.Sprintf(
			"# Concept %03d\n\n"+
				"PR #2731 migration duplicate audit and identifier extraction workflow.\n"+
				"中文记忆：重号迁移、索引一致性、用户沟通偏好与长期决策。\n"+
				"This paragraph repeats representative durable content so chunking, "+
				"BM25 indexing, source validation, and snippet generation process a "+
				"non-trivial corpus. %s\n",
			i, benchmarkDocumentPadding(),
		)
	}
	return files
}

const benchmarkPaddingUnit = "Memory search keeps source files authoritative. " +
	"Search results are locators; callers perform bounded reads and reject stale content. " +
	"Stable insights belong in reflections while repeatable procedures belong in workflows. " +
	"Do not store secrets, credentials, raw transcripts, or temporary execution logs. "

func benchmarkDocumentPadding() string {
	// 18 repetitions yield about 5.2 KiB/file; 200 files plus headings are
	// deterministically ~1.05 MiB, large enough to exercise multi-chunk docs.
	out := ""
	for i := 0; i < 18; i++ {
		out += benchmarkPaddingUnit
	}
	return out
}
