//
//  RetryHandler.swift
//  lichunWebsocket
//
//  Utility for retrying operations with exponential backoff
//

import Foundation

class RetryHandler {
    static let shared = RetryHandler()

    private init() {}

    /// Retry an operation with exponential backoff
    /// - Parameters:
    ///   - maxAttempts: Maximum number of retry attempts (default: 3)
    ///   - initialDelay: Initial delay before first retry in seconds (default: 1.0)
    ///   - maxDelay: Maximum delay between retries in seconds (default: 10.0)
    ///   - operation: The async operation to retry
    /// - Returns: The result of the operation if successful
    /// - Throws: The last error encountered if all retries fail
    func retry<T>(
        maxAttempts: Int = 3,
        initialDelay: TimeInterval = 1.0,
        maxDelay: TimeInterval = 10.0,
        operation: @escaping () async throws -> T
    ) async throws -> T {
        var currentDelay = initialDelay
        var lastError: Error?

        for attempt in 1...maxAttempts {
            do {
                return try await operation()
            } catch {
                lastError = error

                if attempt < maxAttempts {
                    // Wait before retrying
                    try await Task.sleep(nanoseconds: UInt64(currentDelay * 1_000_000_000))

                    // Exponential backoff: double the delay, up to maxDelay
                    currentDelay = min(currentDelay * 2, maxDelay)
                }
            }
        }

        // If we get here, all retries failed
        throw lastError ?? RetryError.maxAttemptsReached
    }

    /// Retry an operation with exponential backoff (non-async version)
    /// - Parameters:
    ///   - maxAttempts: Maximum number of retry attempts (default: 3)
    ///   - initialDelay: Initial delay before first retry in seconds (default: 1.0)
    ///   - maxDelay: Maximum delay between retries in seconds (default: 10.0)
    ///   - operation: The operation to retry
    ///   - completion: Completion handler called with the result
    func retry<T>(
        maxAttempts: Int = 3,
        initialDelay: TimeInterval = 1.0,
        maxDelay: TimeInterval = 10.0,
        operation: @escaping (@escaping (Result<T, Error>) -> Void) -> Void,
        completion: @escaping (Result<T, Error>) -> Void
    ) {
        var currentDelay = initialDelay
        var currentAttempt = 0

        func attemptOperation() {
            currentAttempt += 1

            operation { result in
                switch result {
                case .success(let value):
                    completion(.success(value))

                case .failure(let error):
                    if currentAttempt < maxAttempts {
                        // Schedule retry with exponential backoff
                        DispatchQueue.main.asyncAfter(deadline: .now() + currentDelay) {
                            currentDelay = min(currentDelay * 2, maxDelay)
                            attemptOperation()
                        }
                    } else {
                        // All retries exhausted
                        completion(.failure(error))
                    }
                }
            }
        }

        attemptOperation()
    }
}

// MARK: - Retry Error
enum RetryError: Error {
    case maxAttemptsReached

    var localizedDescription: String {
        switch self {
        case .maxAttemptsReached:
            return "Maximum retry attempts reached"
        }
    }
}

// MARK: - Example Usage
/*
 // Async/await usage:
 Task {
     do {
         let result = try await RetryHandler.shared.retry(maxAttempts: 3) {
             // Your async operation here
             try await someAsyncOperation()
         }
         print("Success: \(result)")
     } catch {
         print("Failed after retries: \(error)")
     }
 }

 // Callback-based usage:
 RetryHandler.shared.retry(maxAttempts: 3) { completion in
     // Your operation here
     someOperation { result in
         completion(result)
     }
 } completion: { result in
     switch result {
     case .success(let value):
         print("Success: \(value)")
     case .failure(let error):
         print("Failed after retries: \(error)")
     }
 }
 */
