//
//
//  PQDownloadManager.swift
//  PQSpeed
//
//  Created by SanW on 2020/8/28.
//  Copyright © 2020 BytesFlow. All rights reserved.
//

import UIKit
import BFCommonKit
import BFNetRequestKit

// MARK: - 下载管理

/// 下载管理

open class PQDownloadManager: NSObject {
    static public let shared = PQDownloadManager()
    public let maxDownloadCount: Int = 1000 // 最大的下载数量
    public var batchDownloadData: [String: Any] = Dictionary<String, Any>.init() // 批量下载数据
    lazy public var sessionManager: PQSessionManager = {
        let sessionManager = PQSessionManager("downloadConfiguration")
        return sessionManager
    }()

    /// <#Description#>
    /// - Parameters:
    ///   - url: <#url description#>
    ///   - name: <#name description#>
    ///   - pathExtension: <#name description#>
    ///   - imageURL: <#imageURL description#>
    ///   - progressHandle: <#progressHandle description#>
    ///   - stateHandle: <#stateHandle description#>
    /// - Returns: <#description#>
    public func download(url: String, name: String? = nil, fileExtensionType: FileExtensionType?, imageURL: String? = nil, progressHandle: @escaping ProgressHandle, stateHandle: @escaping StateHandle) {
        BFLog(message: "开始下载文件：\(url)")
        let subfile = PQDownloadFileManager.downloadTotalFile()
        let newFileExtensionType: FileExtensionType = fileExtensionType ?? (FileExtensionType(rawValue: url.pathExtension) ?? FileExtensionType.normal)
        if (subfile?.count ?? 0) > 0 && (subfile?.count ?? 0) > maxDownloadCount {
            PQDownloadFileManager.removeDownloadFile(url: downloadDirectory + (subfile?.first)!, fileExtensionType: newFileExtensionType)
        }
        if !isValidURL(url: url) {
            BFLog(message: "文件地址为空：\(url)")
            return
        }
        let taskId = url.md5
        let absolutePath = url + ".\(newFileExtensionType.rawValue)"
        let localPath = PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: newFileExtensionType)
        let downloadLenght: Int64 = PQDownloadFileManager.downloadFileLength(url: absolutePath, fileExtensionType: newFileExtensionType)
        let downloadingTask: PQDownloadModel? = sessionManager.downloadTaskDatas.keys.contains(taskId) ? sessionManager.downloadTaskDatas[taskId] : nil
        var totalLength: Int64 = 0
        if downloadingTask != nil {
            totalLength = downloadingTask?.totalLength ?? 0
        }
        if downloadLenght > 0, (downloadingTask != nil && downloadingTask?.state == .compelte) || (totalLength > 0 && totalLength > downloadLenght) {
            progressHandle(1, downloadLenght, totalLength)
            BFLog(message: "文件已下载完成：\(url),downloadLenght = \(downloadLenght),totalLength = \(totalLength)")
            downloadingTask?.state = .compelte
            downloadingTask?.progress = 1
            postNotification(name: cDownloadMatrialSuccessKey, userInfo: ["code": "1", "url": url, "localPath": localPath, "fileExtensionType": newFileExtensionType])
            stateHandle(.compelte, url, localPath, nil)
            return
        }
        if downloadingTask != nil {
            var request = URLRequest(url: URL(string: url)!)
            request.setValue("bytes=%lld-\(downloadLenght)", forHTTPHeaderField: "Range")
            request.setValue("Accept-Encoding", forHTTPHeaderField: "identity")
            let task = sessionManager.session?.dataTask(with: request)
            task?.taskUrl = url
            task?.taskId = taskId
            downloadingTask?.task = task
            downloadingTask?.task?.resume()
            BFLog(message: "下载任务已存在继续下载：\(url),localPath = \(localPath)")
        } else {
            createDirectory(path: downloadDirectory)
            PQDownloadFileManager.removeDownloadFile(url: absolutePath, fileExtensionType: newFileExtensionType)
            BFLog(message: "URL(string: url)! ==\(URL(string: url)!)")
            var request = URLRequest(url: URL(string: url)!)
//            request.setValue("bytes=%lld-\(downloadLenght)", forHTTPHeaderField: "Range")
            request.setValue("Accept-Encoding", forHTTPHeaderField: "identity")
            let task = sessionManager.session?.dataTask(with: request)
            task?.taskUrl = url
            task?.taskId = taskId
            task?.resume()

            let tempModel = PQDownloadModel()
            tempModel.sourceURL = url
            tempModel.fileExtensionType = newFileExtensionType
            tempModel.progress = 0
            tempModel.state = .downloading
            tempModel.name = name
            tempModel.imageURL = imageURL
            tempModel.progressHandle = progressHandle
            tempModel.stateHandle = stateHandle
            tempModel.task = task
            BFLog(message: "新建下载任务：\(url),localPath = \(PQDownloadFileManager.downloadFileLocalPath(url: url, fileExtensionType: newFileExtensionType))，taskID：\(taskId)  tempModel\(String(describing: tempModel.sourceURL))")
            if !sessionManager.downloadTaskDatas.keys.contains(taskId) {
                sessionManager.downloadTaskDatas[taskId] = tempModel
            }
        }
    }

    /// 批量下载
    /// - Parameters:
    ///   - uniqueId: 每个批量下载唯一id
    ///   - urls: 需要下载urls
    ///   - downloadHandle: <#downloadHandle description#>
    /// - Returns: <#description#>
    public  func batchDownload(uniqueId: String, urls: [PQDownloadModel], downloadHandle: @escaping (_ isSuccess: downloadState, _ msg: String?, _ data: [String: Any]?) -> Void) {
        BFLog(message: "urls count is \(urls.count)")
        if urls.count <= 0 {
            return
        }
        if batchDownloadData.keys.contains(uniqueId) {
            BFLog(message: "这组任务已经在下载中\(uniqueId)")
            downloadHandle(.downloading, nil, nil)
            return
        }
        let dispatchGroup = DispatchGroup()
        var downloadInfo: [String: Any] = ["dispatchGroup": dispatchGroup]
        for downloadUrl in urls {
            if isValidURL(url: downloadUrl.sourceURL) {
                DispatchQueue.global().async(group: dispatchGroup, execute: DispatchWorkItem(block: {
                    dispatchGroup.enter()
                    PQDownloadManager.shared.download(url: downloadUrl.sourceURL ?? "", fileExtensionType: downloadUrl.fileExtensionType) { _, _, _ in

                    } stateHandle: { _, _, _, _ in
                    }
                }))
            }
        }
        downloadInfo["urls"] = urls
        downloadInfo["count"] = urls.count
        PQDownloadManager.shared.batchDownloadData[uniqueId] = downloadInfo
        dispatchGroup.notify(queue: DispatchQueue.main) {
            BFLog(message: "所有的已请求完成，tempArr = \(PQDownloadManager.shared.batchDownloadData[uniqueId] ?? [])")
            postNotification(name: cBatchDownloadMatrialSuccessKey, userInfo: ["uniqueId": uniqueId, "urls": PQDownloadManager.shared.batchDownloadData[uniqueId] ?? []])

            PQDownloadManager.shared.batchDownloadData.removeValue(forKey: uniqueId)
        }
    }

    /// 下载素材成功的通知
    /// - Parameter notification: <#notification description#>
    /// - Returns: <#description#>
    @objc public func downloadMarial(notification: Notification) {
        let userInfo = notification.userInfo
        let url = userInfo?["url"] as? String
        let localPath = userInfo?["localPath"] as? String
        let code = userInfo?["code"] as? String
        BFLog(message: "目前下载的任务组数 \(PQDownloadManager.shared.batchDownloadData.keys)")
        PQDownloadManager.shared.batchDownloadData.forEach { _, value in
            var downloadInfo: [String: Any]? = value as? [String: Any]
            let urlsArr: [PQDownloadModel]? = downloadInfo?["urls"] as? [PQDownloadModel]
            let dispatchGroup: DispatchGroup? = downloadInfo?["dispatchGroup"] as? DispatchGroup
            var count: Int = downloadInfo?["count"] as? Int ?? 0
            if (urlsArr?.count ?? 0) > 0 {
                urlsArr?.forEach { downloadModel in
                    if downloadModel.sourceURL == url {
                        if code == "1" {
                            downloadModel.filePath = localPath
                        }
                        BFLog(message: "count = \(count)")
                        if count > 0 {
                            BFLog(message: "leave = \(count)")
                            count = count - 1
                            downloadInfo?["count"] = count
                            let dispatchGroupCount = dispatchGroup.debugDescription.components(separatedBy: ",").filter { $0.contains("count") }.first?.components(separatedBy: CharacterSet.decimalDigits.inverted).compactMap { Int($0) }.first
                            BFLog(message: "dispatchGroup count is \(String(describing: dispatchGroupCount))")
                            dispatchGroup?.leave()
                        }
                    }
                }
            }
        }
    }

    /// 下载背景音乐
    /// - Parameters:
    ///   - url: <#url description#>
    ///   - completionHandler: <#completionHandler description#>
    /// - Returns: <#description#>
    public class func downLoadFile(url: String, completionHandler: @escaping (_ filePath: String?, _ error: Error?) -> Void) {
        // 创建目录
        createDirectory(path: bgMusicDirectory)
        let filePath = bgMusicDirectory + url.md5 + ".mp3"
        let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath))
        if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > 0 {
            DispatchQueue.main.async {
                completionHandler(filePath, nil)
            }
        } else {
            let session = URLSession.shared
            let request = URLRequest(url: URL(string: url)!, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 30.0)
            let dataTask = session.downloadTask(with: request) { url, _, error in
                if url != nil {
                    if FileManager.default.fileExists(atPath: filePath) {
                        try? FileManager.default.removeItem(atPath: filePath)
                    }
                    try? FileManager.default.moveItem(at: url!, to: URL(fileURLWithPath: filePath))
                    DispatchQueue.main.async {
                        completionHandler(filePath, nil)
                    }
                } else {
                    DispatchQueue.main.async {
                        completionHandler(nil, error)
                    }
                }
            }
            dataTask.resume()
        }
    }

    override private init() {
        super.init()
        addNotification(self, selector: #selector(downloadMarial(notification:)), name: cDownloadMatrialSuccessKey, object: nil)
    }

    open override func copy() -> Any {
        return self
    }

    open override func mutableCopy() -> Any {
        return self
    }
}
