// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package runtime_test

import (
	"context"
	"testing"
	"time"

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

	"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
)

//nolint:gocyclo
func TestDrainer(t *testing.T) {
	drainer := runtime.NewDrainer()

	sub1 := drainer.Subscribe()
	sub2 := drainer.Subscribe()

	errCh := make(chan error)

	ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
	defer cancel()

	go func() {
		errCh <- drainer.Drain(ctx)
	}()

	select {
	case <-sub1.EventCh():
	case <-time.After(time.Second):
		require.Fail(t, "should be notified")
	}

	select {
	case <-sub2.EventCh():
	case <-time.After(time.Second):
		require.Fail(t, "should be notified")
	}

	select {
	case <-errCh:
		require.Fail(t, "shouldn't be drained now")
	default:
	}

	sub1.Cancel()

	select {
	case <-errCh:
		require.Fail(t, "shouldn't be drained now")
	default:
	}

	sub3 := drainer.Subscribe()

	select {
	case <-sub3.EventCh():
	case <-time.After(time.Second):
		require.Fail(t, "should be notified")
	}

	sub3.Cancel()
	sub2.Cancel()

	select {
	case err := <-errCh:
		assert.NoError(t, err)
	case <-time.After(time.Second):
		require.Fail(t, "should be drained now")
	}
}

func TestDrainTimeout(t *testing.T) {
	drainer := runtime.NewDrainer()

	drainer.Subscribe()

	errCh := make(chan error)

	ctx, cancel := context.WithTimeout(t.Context(), time.Second)
	defer cancel()

	go func() {
		errCh <- drainer.Drain(ctx)
	}()

	select {
	case err := <-errCh:
		assert.ErrorIs(t, err, context.DeadlineExceeded)
	case <-time.After(5 * time.Second):
		require.Fail(t, "should be drained now")
	}
}
