package storage

import (
	"fmt"
	"testing"
)

func BenchmarkPartSearch(b *testing.B) {
	for _, sparseness := range []int{1, 2, 10, 100} {
		b.Run(fmt.Sprintf("sparseness-%d", sparseness), func(b *testing.B) {
			benchmarkPartSearchWithSparseness(b, sparseness)
		})
	}
}

func benchmarkPartSearchWithSparseness(b *testing.B, sparseness int) {
	blocksCount := 100000
	rows := make([]rawRow, blocksCount)
	for i := 0; i < blocksCount; i++ {
		r := &rows[i]
		r.PrecisionBits = defaultPrecisionBits
		r.TSID.MetricID = uint64(i * sparseness)
		r.Timestamp = int64(i) * 1000
		r.Value = float64(i)
	}
	tr := TimeRange{
		MinTimestamp: rows[0].Timestamp,
		MaxTimestamp: rows[len(rows)-1].Timestamp,
	}
	p := newTestPart(rows)
	for _, tsidsCount := range []int{100, 1000, 10000, 100000} {
		b.Run(fmt.Sprintf("tsids-%d", tsidsCount), func(b *testing.B) {
			tsids := make([]TSID, tsidsCount)
			for i := 0; i < tsidsCount; i++ {
				tsids[i].MetricID = uint64(i)
			}
			benchmarkPartSearch(b, p, tsids, tr, sparseness)
		})
	}
}

func benchmarkPartSearch(b *testing.B, p *part, tsids []TSID, tr TimeRange, sparseness int) {
	b.ReportAllocs()
	b.RunParallel(func(pb *testing.PB) {
		var ps partSearch
		for pb.Next() {
			blocksRead := 0
			ps.Init(p, tsids, tr)
			for ps.NextBlock() {
				blocksRead++
			}
			if err := ps.Error(); err != nil {
				panic(fmt.Errorf("BUG: unexpected error: %w", err))
			}
			blocksWant := len(tsids) / sparseness
			if blocksRead != blocksWant {
				panic(fmt.Errorf("BUG: unexpected blocks read; got %d; want %d", blocksRead, blocksWant))
			}
		}
	})
}