// Package errgrouputil contains methods for working with errgroup code.
package errgrouputil

import (
	"context"
	"runtime"

	"golang.org/x/sync/errgroup"

	"github.com/pomerium/pomerium/pkg/slices"
)

// BuilderFunc is a function that builds a value of type T
type BuilderFunc[T any] func(ctx context.Context) (*T, error)

// Build builds a slice of values of type T using the provided builders concurrently
// and returns the results and any errors.
func Build[T any](
	ctx context.Context,
	builders ...BuilderFunc[T],
) ([]*T, []error) {
	eg, ctx := errgroup.WithContext(ctx)
	eg.SetLimit(runtime.GOMAXPROCS(0)/2 + 1)

	results := make([]*T, len(builders))
	errors := make([]error, len(builders))

	fn := func(i int) func() error {
		return func() error {
			result, err := builders[i](ctx)
			if err != nil {
				errors[i] = err
				return nil
			}
			results[i] = result
			return nil
		}
	}

	for i := range builders {
		eg.Go(fn(i))
	}

	err := eg.Wait()
	if err != nil {
		return nil, []error{err} // not happening
	}

	return slices.Filter(results, func(t *T) bool {
			return t != nil
		}), slices.Filter(errors, func(err error) bool {
			return err != nil
		})
}