import groovy.transform.Field
import com.sonymusic.Utils

@Field
final BACKOFF_STRATEGIES = [
    constant   : { int base, int factor, int attempt -> base },
    linear     : { int base, int factor, int attempt -> base * (attempt + 1) },
    exponential: { int base, int factor, int attempt -> base * (factor ** attempt) },
]

def call(Closure steps) {
    call([:], steps)
}

def call(Map args, Closure steps) {
    def params = Utils.validateParams('withBackoff', args, [
        strategy : [type: String,  required: false, defaultValue: 'constant'],
        attempts : [type: Integer, required: false, defaultValue: 1],
        delay    : [type: Integer, required: false, defaultValue: 0],
        maxDelay : [type: Integer, required: false, defaultValue: Integer.MAX_VALUE],
        factor   : [type: Integer, required: false, defaultValue: 2],
    ])

    assert BACKOFF_STRATEGIES.containsKey(params.strategy): 'Invalid backoff strategy'
    assert params.attempts >= 1: 'attempts must be ≥ 1'
    assert params.delay >= 0: 'delay must be ≥ 0'
    assert params.factor >= 1: 'factor must be ≥ 1'
    assert params.maxDelay >= 0: 'maxDelay must be ≥ 0'

    for (int i = 0; i < params.attempts; i++) {
        try {
            return steps()
        } catch (Exception e) {
            if (i < params.attempts - 1) {
                int computed = BACKOFF_STRATEGIES[params.strategy](params.delay, params.factor, i)
                int delay = Math.min(computed, params.maxDelay)
                echo "Attempt ${i + 1} failed. Retrying in ${delay} seconds..."
                sleep(time: delay, unit: 'SECONDS')
            } else {
                echo "All ${params.attempts} attempts failed."
                throw e
            }
        }
    }
}
