//
//  FetchGitRepositoriesUseCase.swift
//  Domain
//
//  Created by Victor C Tavernari on 02/04/20.
//  Copyright (c) 2020 Taverna Apps. All rights reserved.
//
//  This file was generated by BLU Clean
//

import Foundation

public enum URLCommonsError: LocalizedError {
    case noInternetConnection
    case timeOut
    case generic(String)
}

public enum FetchGitRepositoriesError: LocalizedError {
    case urlError(URLCommonsError)
    case termCannotBeEmpty
}

public protocol FetchGitRepositoriesUseCaseProtocol {
    var delegateInterfaceAdapter: FetchGitRepositoriesInterfaceAdapter? { get set }
    func execute(term: String)
}

// sourcery: AutoMockable
public protocol FetchGitRepositoriesInterfaceAdapter {
    func doing()
    func done(data: [GitRepositoryModel])
    func failure(withError error: FetchGitRepositoriesError)
}

public class FetchGitRepositoriesUseCase: FetchGitRepositoriesUseCaseProtocol {
    public var delegateInterfaceAdapter: FetchGitRepositoriesInterfaceAdapter?
    private let gitRepoRepository: GitRepoRepositoryProtocol
    private let reliabilityCalculatorUseCase: ReliabilityRepoCalculatorUseCase

    public init(
        gitRepoRepository: GitRepoRepositoryProtocol,
        reliabilityCalculatorUseCase: ReliabilityRepoCalculatorUseCase
    ) {
        self.gitRepoRepository = gitRepoRepository
        self.reliabilityCalculatorUseCase = reliabilityCalculatorUseCase
    }

    private func fetchRepositories(term: String, completion: @escaping ([GitRepositoryModel]) -> Void) {
        gitRepoRepository.list(term: term) { result in
            do {
                let repositories = try result.handle()
                completion(repositories)
            } catch let error as URLError {
                self.delegateInterfaceAdapter?.failure(withError: .urlError(error.makeCommonError()))
            } catch {
                self.delegateInterfaceAdapter?.failure(withError: .urlError(.generic(error.localizedDescription)))
            }
        }
    }

    private func fetchReliabilityConfig(completion: @escaping (GitRepoReliabilityMultiplierModel) -> Void) {
        gitRepoRepository.getRepoReliabilityMultiplier { result in
            do {
                let repoReliabilityMultiplierModel = try result.handle()
                completion(repoReliabilityMultiplierModel)
            } catch let error as URLError {
                self.delegateInterfaceAdapter?.failure(withError: .urlError(error.makeCommonError()))
            } catch {
                self.delegateInterfaceAdapter?.failure(withError: .urlError(.generic(error.localizedDescription)))
            }
        }
    }

    public func execute(term: String) {
        guard term.isEmpty == false else {
            delegateInterfaceAdapter?.failure(withError: FetchGitRepositoriesError.termCannotBeEmpty)
            return
        }

        delegateInterfaceAdapter?.doing()
        fetchRepositories(term: term) { repositories in
            self.fetchReliabilityConfig { repoReliabilityMultiplierModel in
                var repoResult = repositories
                if repoReliabilityMultiplierModel.enable {
                    repoResult = repoResult.map { repo -> GitRepositoryModel in
                        var tempRepo = repo
                        let multiplier = repoReliabilityMultiplierModel.multiplier
                        let enable = repoReliabilityMultiplierModel.enable
                        tempRepo.reliability.isEnable = enable
                        let stats = repo.stats
                        tempRepo.reliability.score = self.reliabilityCalculatorUseCase.execute(repoStats: stats,
                                                                                               multiplier: multiplier)
                        return tempRepo
                    }
                }

                self.delegateInterfaceAdapter?.done(data: repoResult)
            }
        }
    }
}
