// Copyright 2024 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.

import 'dart:async';

/// Runs [callback] and retries if an exception is thrown.
///
/// * [maxRetries] the maximum number of times [callback] will be ran before
///   rethrowing an exception if it does not complete successfully.
/// * [retryDelay] the time to wait between retry attempts.
/// * [stopCondition] an optional callback that determines whether we should
///   stop retrying, in addition to the condition that we must not retry
///   more than [maxRetries] times. If the [stopCondition] is met, we will stop
///   retrying without exception.
/// * [onRetry] an optional callback that will be called if [callback] fails
///   and we need to attempt a retry.
Future<void> runWithRetry({
  required FutureOr<void> Function() callback,
  required int maxRetries,
  Duration retryDelay = const Duration(milliseconds: 250),
  FutureOr<bool> Function()? stopCondition,
  FutureOr<void> Function(int attempt)? onRetry,
}) async {
  for (var attempt = 1;
      attempt <= maxRetries && (await stopCondition?.call() != true);
      attempt++) {
    try {
      await callback();
      break;
    } catch (e) {
      if (attempt == maxRetries) {
        rethrow;
      }
      await onRetry?.call(attempt);
      await Future.delayed(retryDelay);
    }
  }
}
