import 'dart:math';

import 'package:logging/logging.dart';

import 'package:pshared/utils/exception.dart';


class RetryHelper {
  static final _logger = Logger('auth.retry');

  /// Executes an operation with exponential backoff retry logic
  static Future<T> withExponentialBackoff<T>(
    Future<T> Function() operation, {
    int maxRetries = 3,
    Duration initialDelay = const Duration(milliseconds: 500),
    double backoffMultiplier = 2.0,
    Duration maxDelay = const Duration(seconds: 30),
    bool Function(Exception)? shouldRetry,
  }) async {
    Exception? lastException;
    
    // Total attempts = initial attempt + maxRetries
    final totalAttempts = maxRetries + 1;
    
    for (int attempt = 1; attempt <= totalAttempts; attempt++) {
      try {
        _logger.fine('Attempting operation (attempt $attempt/$totalAttempts)');
        return await operation();
      } catch (e) {
        lastException = toException(e);
        
        // Don't retry if we've reached max attempts
        if (attempt == totalAttempts) {
          _logger.warning('Operation failed after $totalAttempts attempts: $lastException');
          rethrow;
        }
        
        // Check if we should retry this specific error
        if (shouldRetry != null && !shouldRetry(lastException)) {
          _logger.fine('Operation failed with non-retryable error: $lastException');
          rethrow;
        }
        
        // Calculate delay with exponential backoff
        final delayMs = min(
          initialDelay.inMilliseconds * pow(backoffMultiplier, attempt - 1).toInt(),
          maxDelay.inMilliseconds,
        );
        final delay = Duration(milliseconds: delayMs);
        
        _logger.fine('Operation failed (attempt $attempt), retrying in ${delay.inMilliseconds}ms: $lastException');
        await Future.delayed(delay);
      }
    }
    
    // This should never be reached due to rethrow above, but just in case
    throw lastException ?? Exception('Retry logic error');
  }

  /// Determines if an error is retryable (network/temporary errors)
  static bool isRetryableError(Exception error) {
    final errorString = error.toString().toLowerCase();
    
    // Network connectivity issues
    if (errorString.contains('socket') ||
        errorString.contains('connection') ||
        errorString.contains('timeout') ||
        errorString.contains('network')) {
      return true;
    }
    
    // Server temporary errors (5xx)
    if (errorString.contains('500') ||
        errorString.contains('502') ||
        errorString.contains('503') ||
        errorString.contains('504')) {
      return true;
    }
    
    // Rate limiting
    if (errorString.contains('429')) {
      return true;
    }
    
    return false;
  }
}