package viper

import (
	"math/rand"
	"time"

	"k8s.io/utils/integer"
)

type Backoff struct {
	defaultDuration time.Duration
	maxDuration     time.Duration
	backoff         time.Duration
	maxJitterFactor float64
	rand            *rand.Rand
}

func NewBackOff(initial, max time.Duration, maxJitterFactor float64) *Backoff {
	return &Backoff{
		defaultDuration: initial,
		maxDuration:     max,
		backoff:         initial,
		maxJitterFactor: maxJitterFactor,
		rand:            rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

// Next returns the current backoff Duration and increment the internal
func (p *Backoff) Next() time.Duration {
	d := p.backoff
	delay := d * 2       // exponential
	delay += p.jitter(d) // add some jitter to the delay
	p.backoff = time.Duration(integer.Int64Min(int64(delay), int64(p.maxDuration)))
	return d
}

// Reset resets the internal backoff to the initial value
func (p *Backoff) Reset() {
	p.backoff = p.defaultDuration
}

func (p *Backoff) jitter(delay time.Duration) time.Duration {
	return time.Duration(p.rand.Float64() * p.maxJitterFactor * float64(delay))
}
