package system

import (
	"context"
	"time"

	"github.com/moby/moby/client"
	"gotest.tools/v3/poll"
)

// WaitForStableGoroutineCount polls the daemon Info API and returns the reported goroutine count
// after multiple calls return the same number.
func WaitForStableGoroutineCount(ctx context.Context, t poll.TestingT, apiClient client.SystemAPIClient, opts ...poll.SettingOp) int {
	var out int
	// Use a longish delay to make sure the goroutine count is actually stable.
	defaults := []poll.SettingOp{poll.WithTimeout(time.Minute), poll.WithDelay(time.Second)}
	opts = append(defaults, opts...)

	poll.WaitOn(t, StableGoroutineCount(ctx, apiClient, &out), opts...)
	return out
}

// StableGoroutineCount is a [poll.Check] that polls the daemon info API until the goroutine count is the same for 3 iterations.
func StableGoroutineCount(ctx context.Context, apiClient client.SystemAPIClient, count *int) poll.Check {
	var (
		numStable int
		nRoutines int
	)

	return func(t poll.LogT) poll.Result {
		n, err := getGoroutineNumber(ctx, apiClient)
		if err != nil {
			return poll.Error(err)
		}

		last := nRoutines

		if nRoutines == n {
			numStable++
		} else {
			numStable = 0
			nRoutines = n
		}

		if numStable > 3 {
			*count = n
			return poll.Success()
		}
		return poll.Continue("goroutine count is not stable: last %d, current %d, stable iters: %d", last, n, numStable)
	}
}

// CheckGoroutineCount returns a [poll.Check] that polls the daemon info API until the expected number of goroutines is hit.
func CheckGoroutineCount(ctx context.Context, apiClient client.SystemAPIClient, expected int) poll.Check {
	first := true
	return func(t poll.LogT) poll.Result {
		n, err := getGoroutineNumber(ctx, apiClient)
		if err != nil {
			return poll.Error(err)
		}
		if n > expected {
			if first {
				t.Log("Waiting for goroutines to stabilize")
				first = false
			}
			return poll.Continue("expected %d goroutines, got %d", expected, n)
		}
		return poll.Success()
	}
}

func getGoroutineNumber(ctx context.Context, apiClient client.SystemAPIClient) (int, error) {
	result, err := apiClient.Info(ctx, client.InfoOptions{})
	if err != nil {
		return 0, err
	}
	return result.Info.NGoroutines, nil
}
