//
//  NCNetworking+Synchronization.swift
//  Nextcloud
//
//  Created by Marino Faggiana on 07/02/24.
//  Copyright © 2024 Marino Faggiana. All rights reserved.
//
//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

import UIKit
import JGProgressHUD
import NextcloudKit
import Alamofire

extension NCNetworking {

    func synchronization(account: String,
                         serverUrl: String,
                         add: Bool,
                         completion: @escaping (_ errorCode: Int, _ items: Int) -> Void = { _, _ in }) {

        let startDate = Date()
        let options = NKRequestOptions(timeout: 120, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
        NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl,
                                             depth: "infinity",
                                             showHiddenFiles: NCKeychain().showHiddenFiles,
                                             options: options) { resultAccount, files, _, error in

            guard account == resultAccount else { return }
            var metadatasDirectory: [tableMetadata] = []
            var metadatasSynchronizationOffline: [tableMetadata] = []
            if !add {
                if NCManageDatabase.shared.getResultMetadata(predicate: NSPredicate(format: "account == %@ AND sessionSelector == %@ AND (status == %d OR status == %d)", account, NCGlobal.shared.selectorSynchronizationOffline, NCGlobal.shared.metadataStatusWaitDownload, NCGlobal.shared.metadataStatusDownloading)) != nil { return }
            }

            if error == .success {
                for file in files {
                    if file.directory {
                        metadatasDirectory.append(NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: false))
                    } else if self.isSynchronizable(ocId: file.ocId, fileName: file.fileName, etag: file.etag) {
                        metadatasSynchronizationOffline.append(NCManageDatabase.shared.convertFileToMetadata(file, isDirectoryE2EE: false))
                    }
                }
                NCManageDatabase.shared.addMetadatas(metadatasDirectory)
                NCManageDatabase.shared.setMetadatasSessionInWaitDownload(metadatas: metadatasSynchronizationOffline,
                                                                          session: NCNetworking.shared.sessionDownloadBackground,
                                                                          selector: NCGlobal.shared.selectorSynchronizationOffline)
                NCManageDatabase.shared.setDirectorySynchronizationDate(serverUrl: serverUrl, account: account)
                let diffDate = Date().timeIntervalSinceReferenceDate - startDate.timeIntervalSinceReferenceDate
                NextcloudKit.shared.nkCommonInstance.writeLog("[LOG] Synchronization \(serverUrl) in \(diffDate)")
                completion(0, metadatasSynchronizationOffline.count)
            } else {
                NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Synchronization \(serverUrl), \(error.errorCode), \(error.description)")
                completion(error.errorCode, metadatasSynchronizationOffline.count)
            }
        }
    }

    @discardableResult
    func synchronization(account: String, serverUrl: String, add: Bool) async -> (Int, Int) {
        await withUnsafeContinuation({ continuation in
            synchronization(account: account, serverUrl: serverUrl, add: add) { errorCode, items in
                continuation.resume(returning: (errorCode, items))
            }
        })
    }

    func isSynchronizable(ocId: String, fileName: String, etag: String) -> Bool {
        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
           (metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusWaitDownload) {
            return false
        }
        let localFile = NCManageDatabase.shared.getResultsTableLocalFile(predicate: NSPredicate(format: "ocId == %@", ocId))?.first
        if localFile?.etag != etag || NCUtilityFileSystem().fileProviderStorageSize(ocId, fileNameView: fileName) == 0 {
            return true
        } else {
            return false
        }
    }
}