package backoff

import (
	"context"
	"fmt"
	"math"
	"time"

	"code.justin.tv/eventbus/controlplane/internal/logger"

	"github.com/pkg/errors"
	"go.uber.org/zap"
)

func Backoff(iteration int) time.Duration {
	return time.Duration(math.Pow(float64(2), float64(iteration))) * time.Second
}

func RetryWithBackoff(ctx context.Context, op string, maxAttempts int, f func() error) error {
	var err error
	log := logger.FromContext(ctx)
	for n := 1; n <= maxAttempts; n++ {
		err = f()
		if err == nil {
			return nil
		} else if n != maxAttempts {
			duration := Backoff(n - 1)
			log.Info(fmt.Sprintf("%s attempt %d/%d failed with %s, trying again in %s", op, n, maxAttempts, err.Error(), duration.String()))
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(duration):
				continue
			}
		}
	}
	log.Info("exceeded max retries", zap.String("operation", op), zap.String("error", err.Error()))
	return errors.Wrap(err, "exceeded max retry count")
}
