package syncz

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"k8s.io/apimachinery/pkg/util/wait"
)

func TestSubscriptions_DispatchingMultiple(t *testing.T) {
	// GIVEN
	var wg wait.Group
	defer wg.Wait()
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	s := NewSubscriptions[int]()
	x := 42
	// recorder for callback hits
	rec1 := make(chan struct{})
	rec2 := make(chan struct{})
	subscriber1 := func(_ context.Context, e int) {
		assert.Equal(t, x, e)
		close(rec1)
	}
	subscriber2 := func(_ context.Context, e int) {
		assert.Equal(t, x, e)
		close(rec2)
	}

	// WHEN
	assert.Zero(t, s.Len())
	// starting multiple subscribers
	wg.Start(func() {
		s.On(ctx, subscriber1)
	})
	wg.Start(func() {
		s.On(ctx, subscriber2)
	})

	// give the OnGitPushEvent goroutines time to be scheduled and registered
	assert.Eventually(t, func() bool {
		return s.Len() == 2
	}, time.Minute, time.Millisecond)

	// dispatch a single event
	s.Dispatch(ctx, x)

	// THEN
	<-rec1
	<-rec2

	cancel()
	assert.Eventually(t, func() bool {
		return s.Len() == 0
	}, time.Minute, time.Millisecond)
}

func TestSubscriptions_ConcurrentCancel(t *testing.T) {
	s := NewSubscriptions[int]()

	ctx, cancel := context.WithCancel(context.Background())
	cancel() // canceled right here

	var wg wait.Group
	defer wg.Wait()

	for range 10 {
		wg.Start(func() {
			s.On(ctx, func(ctx context.Context, e int) {})
		})
		wg.Start(func() {
			s.Dispatch(context.Background(), 42)
		})
	}
}

func BenchmarkSub(b *testing.B) {
	b.ReportAllocs()
	s := NewSubscriptions[int64]()
	var l Listen[int64]
	for b.Loop() {
		l = s.Subscribe(context.Background())
	}
	_ = l
}
