package internal

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCancellableGroupContext(t *testing.T) {
	cgc := NewCancellableGroupContext()

	ctx1, cancel1 := cgc.WithContext(context.Background())
	defer cancel1()

	ctx2, cancel2 := cgc.WithContext(ctx1)
	defer cancel2()

	ctx3, cancel3 := cgc.WithContext(context.Background())
	defer cancel3()

	ctx4, cancel4 := cgc.WithContext(ctx3)
	defer cancel4()

	require.NoError(t, ctx1.Err())
	require.NoError(t, ctx2.Err())
	require.NoError(t, ctx3.Err())
	require.NoError(t, ctx4.Err())

	cancel1()
	require.ErrorIs(t, ctx1.Err(), context.Canceled)
	require.ErrorIs(t, context.Cause(ctx1), context.Canceled)
	require.ErrorIs(t, ctx2.Err(), context.Canceled)
	require.ErrorIs(t, context.Cause(ctx2), context.Canceled)
	require.NoError(t, ctx3.Err())
	require.NoError(t, ctx4.Err())

	expectedErr := fmt.Errorf("cancel all the things")
	cgc.Cancel(expectedErr)

	require.EventuallyWithT(t, func(t *assert.CollectT) {
		assert.ErrorIs(t, ctx1.Err(), context.Canceled)
		assert.ErrorIs(t, context.Cause(ctx1), context.Canceled)
		assert.ErrorIs(t, ctx2.Err(), context.Canceled)
		assert.ErrorIs(t, context.Cause(ctx2), context.Canceled)

		assert.ErrorIs(t, ctx3.Err(), context.Canceled)
		assert.ErrorIs(t, context.Cause(ctx3), expectedErr)
		assert.ErrorIs(t, ctx4.Err(), context.Canceled)
		assert.ErrorIs(t, context.Cause(ctx4), expectedErr)
	}, 5*time.Second, time.Second)
}
