import '../../contracts/trakt/trakt_response.dart';

enum TraktRetryDecision {

  complete,


  discard,


  retry,


  giveUp,
}

class TraktRetryOutcome {
  final TraktRetryDecision decision;
  final Duration? delay;
  const TraktRetryOutcome(this.decision, {this.delay});
}


class TraktRetryPolicy {
  TraktRetryPolicy({
    this.maxRetries = 8,
    this.baseBackoff = const Duration(seconds: 5),
    this.maxBackoff = const Duration(minutes: 30),
  });


  final int maxRetries;
  final Duration baseBackoff;
  final Duration maxBackoff;

  TraktRetryOutcome evaluate({
    required TraktResponseClass cls,
    required int retryCount,
    Duration? retryAfter,
  }) {
    switch (cls) {
      case TraktResponseClass.success:
      case TraktResponseClass.duplicate:
        return const TraktRetryOutcome(TraktRetryDecision.complete);
      case TraktResponseClass.invalid:
        return const TraktRetryOutcome(TraktRetryDecision.discard);
      case TraktResponseClass.rateLimited:
        if (retryCount >= maxRetries) {
          return const TraktRetryOutcome(TraktRetryDecision.giveUp);
        }
        return TraktRetryOutcome(
          TraktRetryDecision.retry,
          delay: retryAfter ?? backoff(retryCount),
        );
      case TraktResponseClass.unauthorized:
      case TraktResponseClass.transient:
        if (retryCount >= maxRetries) {
          return const TraktRetryOutcome(TraktRetryDecision.giveUp);
        }
        return TraktRetryOutcome(
          TraktRetryDecision.retry,
          delay: backoff(retryCount),
        );
    }
  }


  Duration backoff(int retryCount) {
    final shift = retryCount.clamp(0, 20);
    final millis = baseBackoff.inMilliseconds * (1 << shift);
    if (millis >= maxBackoff.inMilliseconds || millis < 0) return maxBackoff;
    return Duration(milliseconds: millis);
  }
}
