package util

import (
	"errors"
	"testing"
	"time"
)

func TestExecuteParallelTaskGroups(t *testing.T) {
	firstTaskStarted := make(chan struct{})
	firstTaskRelease := make(chan struct{})
	secondTaskStarted := make(chan struct{})
	secondTaskInFirstGroupStarted := make(chan struct{})
	done := make(chan error, 1)

	go func() {
		done <- ExecuteParallelTaskGroups(2, [][]ErrorWaitGroupTask{
			{
				func() error {
					close(firstTaskStarted)
					<-firstTaskRelease
					return nil
				},
				func() error {
					close(secondTaskInFirstGroupStarted)
					return nil
				},
			},
			{
				func() error {
					close(secondTaskStarted)
					return nil
				},
			},
		})
	}()

	select {
	case <-firstTaskStarted:
	case <-time.After(time.Second):
		t.Fatal("first task did not start")
	}
	select {
	case <-secondTaskStarted:
	case <-time.After(time.Second):
		t.Fatal("independent task group did not start in parallel")
	}
	select {
	case <-secondTaskInFirstGroupStarted:
		t.Fatal("tasks in the same group ran in parallel")
	default:
	}

	close(firstTaskRelease)
	select {
	case err := <-done:
		if err != nil {
			t.Fatalf("execute parallel task groups: %v", err)
		}
	case <-time.After(time.Second):
		t.Fatal("parallel task groups did not finish")
	}
}

func TestExecuteParallelTaskGroupsStopsOnlyFailedGroup(t *testing.T) {
	expectedErr := errors.New("move failed")
	failedGroupContinued := false
	otherGroupRan := false

	err := ExecuteParallelTaskGroups(1, [][]ErrorWaitGroupTask{
		{
			func() error { return expectedErr },
			func() error {
				failedGroupContinued = true
				return nil
			},
		},
		{
			func() error {
				otherGroupRan = true
				return nil
			},
		},
	})
	if !errors.Is(err, expectedErr) {
		t.Fatalf("expected task error, got %v", err)
	}
	if failedGroupContinued {
		t.Fatal("task after a failed task in the same group ran")
	}
	if !otherGroupRan {
		t.Fatal("independent task group did not run")
	}
}
