import 'package:nextcloud/nextcloud.dart';

/// True for errors worth a short automatic retry: HTTP 5xx thrown by the
/// nextcloud/dynamite clients. Our instance intermittently answers the slow
/// root PROPFIND with a quick empty 500 via nginx that succeeds on retry.
/// Timeouts are deliberately not transient — the root listing already has a
/// multi-minute ceiling, so repeating it would multiply the wait.
bool isTransientServerError(Object error) =>
    error is DynamiteApiException &&
    error.statusCode >= 500 &&
    error.statusCode <= 599;

Duration _defaultRetryDelay(int retry) =>
    retry == 1 ? const Duration(seconds: 1) : const Duration(seconds: 3);

/// Runs [attempt] up to [maxAttempts] times, waiting [delayFor] between
/// tries. Errors that don't match [shouldRetry] and the final failure are
/// rethrown unchanged. [onRetry] fires before each wait with the 1-based
/// number of the upcoming attempt, e.g. `(2, 3)` — used to surface retry
/// progress in the UI. [delayFor] receives the 1-based index of the retry
/// being scheduled and is injectable so tests run instantly.
Future<T> retryOnTransientError<T>(
  Future<T> Function() attempt, {
  int maxAttempts = 3,
  Duration Function(int retry) delayFor = _defaultRetryDelay,
  bool Function(Object error) shouldRetry = isTransientServerError,
  void Function(int nextAttempt, int maxAttempts)? onRetry,
}) async {
  for (var attemptNo = 1; ; attemptNo++) {
    try {
      return await attempt();
    } catch (e) {
      if (attemptNo >= maxAttempts || !shouldRetry(e)) rethrow;
      onRetry?.call(attemptNo + 1, maxAttempts);
      await Future<void>.delayed(delayFor(attemptNo));
    }
  }
}
