import Foundation

class DownloadBatch {

static let kMaximumDownloadsCount = 3

private let images: [String: String]
private var imagesLeft = [String: String]()
private let downloadGroup = DispatchGroup()
private let session = URLSession.shared
private var imageData = [String: Data]()
private var currentDownloadKeys = Set<String>()
private let url: URL
private let syncQueue = DispatchQueue(label: "download_image_q")
private var isFinished = false

init(images: [String: String], url: URL) {
        self.images = images
        self.imagesLeft = images
        self.url = url
}

func download() -> [Figma.PageId: Data] {
        self.downloadGroup.enter()
        self.downloadNext()
        self.downloadGroup.wait()
        return self.imageData
}

private func downloadNext() {
        let isFinished = self.syncQueue.sync {
                self.imagesLeft.isEmpty && self.currentDownloadKeys.isEmpty && !self.isFinished
        }
        let canDonwloadMore = self.syncQueue.sync {
                self.currentDownloadKeys.count < DownloadBatch.kMaximumDownloadsCount
        }
        if isFinished {
                self.isFinished = true
                print("Download batch finished: \(self.images)")
                self.downloadGroup.leave()
        } else if canDonwloadMore {

                if let first = self.imagesLeft.first {

                        self.syncQueue.sync {
                                self.imagesLeft.removeValue(forKey: first.key)
                                self.currentDownloadKeys.insert(first.key)
                        }
                        self.downloadItem(key: first.key, value: first.value, retryCount: 5) { data in
                                self.syncQueue.sync {
                                        self.imageData[first.key] = data
                                        _ = self.currentDownloadKeys.remove(first.key)
                                }
                                self.downloadNext()
                        }
                        self.downloadNext()
                }
        }
}

private func downloadItem(key: String, value: String, retryCount: Int, completion: @escaping (Data?) -> Void) {
        let data = self.syncQueue.sync {
                self.imageData[key]
        }
        if data != nil {
                completion(data); return
        }
        if retryCount < 0 {
                print("⛔️ Download image \(value) retry count limit")
                completion(nil); return
        }

        let fileUrl = self.url.appendingPathComponent(value.cacheName)

        if let data = try? Data(contentsOf: fileUrl) {
                print("✅ Image already exist at \(value.cacheName), skip download \(value)")
                completion(data)
                return
        }

        let imageURL = URL(string: value)!
        print("⬇️ Download image(\(retryCount)) with url: \(value)")
        let request = URLRequest(
                url: imageURL,
                cachePolicy: .reloadIgnoringLocalCacheData,
                timeoutInterval: 7 * 60
        )
        self.session.downloadTask(with: request) { (url, r, e) in
                if let url = url {
                        do {
                                let data = try Data(contentsOf: url)
                                try data.write(to: fileUrl)
                                print("✅ Did finish \(value) at \(value.cacheName)")
                                completion(data)
                        } catch {
                                print("⛔️ Did fail download, retry: \(value), \(error)")
                                self.downloadItem(key: key, value: value, retryCount: retryCount - 1, completion: completion)
                        }
                } else {
                        if let error = e {
                                print("⛔️ Did fail download, retry: \(value), \(error)")
                        }
                        self.downloadItem(key: key, value: value, retryCount: retryCount - 1, completion: completion)
                }
        }.resume()
}

}