//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Amplify
import Foundation

/// All methods in this extension must be invoked from workQueue (as in during a `respond` call
extension RemoteSyncEngine {

    func resetCurrentAttemptNumber() {
        currentAttemptNumber = 1
    }

    func scheduleRestartOrTerminate(error: AmplifyError) {
        Self.log.debug("scheduling restart or terminate on error: \(error)")
        let advice = getRetryAdvice(error: error)
        if advice.shouldRetry {
            scheduleRestart(advice: advice)
        } else {
            remoteSyncTopicPublisher.send(completion: .failure(DataStoreError.api(error)))
            if let completionBlock = finishedCompletionBlock {
                completionBlock(.failure(causedBy: error))
                finishedCompletionBlock = nil
            }
        }
    }

    private func getRetryAdvice(error: Error) -> RequestRetryAdvice {
        var urlErrorOptional: URLError?
        if let dataStoreError = error as? DataStoreError,
            let underlyingError = dataStoreError.underlyingError as? URLError {
            urlErrorOptional = underlyingError
        } else if let urlError = error as? URLError {
            urlErrorOptional = urlError
        } else if let dataStoreError = error as? DataStoreError,
                  case .api(let amplifyError, _) = dataStoreError,
                  let apiError = amplifyError as? APIError,
                  case .networkError(_, _, let error) = apiError,
                  let urlError = error as? URLError {
            urlErrorOptional = urlError
        }

        let advice = requestRetryablePolicy.retryRequestAdvice(
            urlError: urlErrorOptional,
            httpURLResponse: nil,
            attemptNumber: currentAttemptNumber
        )
        return advice
    }

    private func scheduleRestart(advice: RequestRetryAdvice) {
        log.verbose("\(#function) scheduling retry for restarting remote sync engine")
        remoteSyncTopicPublisher.send(.schedulingRestart)
        resolveReachabilityPublisher()
        mutationRetryNotifier = MutationRetryNotifier(
            advice: advice,
            networkReachabilityPublisher: networkReachabilityPublisher
        ) {
            self.taskQueue.async {
                self.mutationRetryNotifier = nil
                self.stateMachine.notify(action: .scheduledRestartTriggered)
            }
        }
        currentAttemptNumber += 1
    }

    private func resolveReachabilityPublisher() {
        if networkReachabilityPublisher == nil {
            if let reachability = api as? APICategoryReachabilityBehavior {
                do {
                    networkReachabilityPublisher = try reachability.reachabilityPublisher()
                } catch {
                    log.error("\(#function): Unable to listen on reachability: \(error)")
                }
            }
        }
    }
}
