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

import UIKit
import BFCommonKit
import BFNetRequestKit

open class PQSessionManager: NSObject {
    public var downloadTaskDatas: [String: PQDownloadModel] = Dictionary<String, PQDownloadModel>.init()
    public var dispatchQueue: DispatchQueue! // 线程
    public var session: URLSession? // sesstion
    public var identifier: String = "" // 标示
    public var completionHandler: (() -> Void)? // 完成回调
    public lazy var configuration: URLSessionConfiguration = { // sesstion 配置
        //        let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
        let configuration = URLSessionConfiguration.default
        configuration.isDiscretionary = false
        configuration.timeoutIntervalForRequest = 30 // 请求超时时长，默认是60s
        configuration.timeoutIntervalForResource = 7 * 24 * 60 * 60 // 资源超时时长，默认是7天，也就是说资源要在7天内到达
        configuration.httpMaximumConnectionsPerHost = 20
        configuration.allowsCellularAccess = true // 是否支持蜂窝网络下载
        if #available(iOS 13, *) {
            configuration.allowsConstrainedNetworkAccess = true // 是否使用受限制的网络
            configuration.allowsExpensiveNetworkAccess = true // 是否使用昂贵的网络
        }
        return configuration
    }()

    public init(_ identifier: String,
                configuration: URLSessionConfiguration? = nil,
                dispatchQueue: DispatchQueue = DispatchQueue(label: "com.piaoquan.pqspeed.dispatchQueue"))
    {
        super.init()
        let bundleIdentifier = Bundle.main.bundleIdentifier ?? "com.piaoquan.pqspeed"
        if configuration != nil {
            self.configuration = configuration!
        }
        self.identifier = "\(bundleIdentifier).\(identifier)"
        self.dispatchQueue = dispatchQueue
        createSession()
    }

    func createSession() {
        let delegateQueue = OperationQueue(maxConcurrentOperationCount: 4, underlyingQueue: dispatchQueue, name: "com.piaoquan.pqspeed.delegateQueue")
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: delegateQueue)
    }
}

extension PQSessionManager: URLSessionDataDelegate {
    /// 收到请求响应
    /// - Parameters:
    ///   - session: <#session description#>
    ///   - dataTask: <#dataTask description#>
    ///   - response: <#response description#>
    ///   - completionHandler: <#completionHandler description#>
   public func urlSession(_: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        let taskId = dataTask.taskId
        let taskUrl = dataTask.taskUrl
        let downloadData = downloadTaskDatas[taskId]
        let fileExtensionType: FileExtensionType = downloadData?.fileExtensionType ?? (FileExtensionType(rawValue: taskUrl.pathExtension) ?? FileExtensionType.normal)
        let absolutePath = taskUrl + ".\(fileExtensionType.rawValue)"
        let expectedLength: Int64 = response.expectedContentLength
        let downloadedLength: Int64 = PQDownloadFileManager.downloadFileLength(url: absolutePath, fileExtensionType: fileExtensionType)
        let totalLength: Int64 = expectedLength
        if totalLength == 0 {
            if downloadData?.progressHandle != nil {
                downloadData?.progressHandle!(0, downloadedLength, totalLength)
            }
            if downloadData?.stateHandle != nil {
                downloadData?.stateHandle!(.error, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), PQError(msg: "下载文件为空", code: 0))
            }
            BFLog(message: "收到下载请求-下载文件为空：\(taskUrl),localPath = \(PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType))")
            return
        }
        // 创建文件地址
        let filePath = PQDownloadFileManager.createDownloadFilePath(url: absolutePath, fileExtensionType: fileExtensionType)
        let fileHandle: FileHandle? = FileHandle(forWritingAtPath: filePath)
        downloadData?.downloadLength = downloadedLength
        downloadData?.totalLength = totalLength
        downloadData?.fileHandle = fileHandle
        downloadData?.filePath = filePath
        downloadData?.mimeType = response.mimeType
        if downloadData?.name == nil {
            downloadData?.name = response.suggestedFilename
        }
        BFLog(message: "收到下载请求-下载文件：downloadedLength = \(downloadedLength),localPath = \(filePath)，totalLength = \(totalLength),expectedLength = \(expectedLength)")
        completionHandler(.allow)
    }

    /// 收到数据
    /// - Parameters:
    ///   - session: <#session description#>
    ///   - dataTask: <#dataTask description#>
    ///   - data: <#data description#>
    public func urlSession(_: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        let taskId = dataTask.taskId
        let taskUrl = dataTask.taskUrl
        let downloadData = downloadTaskDatas[taskId]
        let fileExtensionType: FileExtensionType = downloadData?.fileExtensionType ?? (FileExtensionType(rawValue: taskUrl.pathExtension) ?? FileExtensionType.normal)
        let absolutePath = taskUrl + ".\(fileExtensionType.rawValue)"
        BFLog(message: "收到下载请求-收到data taskId = \(taskId),data = \(data),totalBytes = \(downloadData?.totalLength ?? 0),download = \(PQDownloadFileManager.downloadFileLength(url: absolutePath, fileExtensionType: fileExtensionType))")

        if downloadData != nil {
            downloadData?.fileHandle?.seekToEndOfFile()
            downloadData?.fileHandle?.write(data)
            downloadData?.downloadLength = (downloadData?.downloadLength ?? 0) + Int64(data.count)
            let progress = Float(downloadData?.downloadLength ?? 0) / Float(downloadData?.totalLength ?? 1)
            downloadData?.progress = progress
            downloadData?.state = .downloading
            if downloadData?.progressHandle != nil {
                downloadData?.progressHandle!(progress, downloadData?.downloadLength, downloadData?.totalLength)
            }
            if downloadData?.stateHandle != nil {
                downloadData?.stateHandle!(.downloading, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), nil)
            }
        }
    }

    /// 下载完成/终止
    /// - Parameters:
    ///   - session: <#session description#>
    ///   - task: <#task description#>
    ///   - error: <#error description#>
    public func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        let taskId = task.taskId
        let taskUrl = task.taskUrl
        let downloadData = downloadTaskDatas[taskId]
        var fileExtensionType: FileExtensionType = downloadData?.fileExtensionType ?? (FileExtensionType(rawValue: taskUrl.pathExtension) ?? FileExtensionType.normal)
        let absolutePath = taskUrl + ".\(fileExtensionType.rawValue)"
        downloadData?.fileHandle?.closeFile()
        BFLog(message: "收到下载请求-下载完成 taskId = \(taskId),error = \(error ?? PQError(msg: ""))")
        downloadData?.fileHandle = nil
        if downloadData?.realFileExtensionType != nil, downloadData?.fileExtensionType != nil, downloadData?.realFileExtensionType != downloadData?.fileExtensionType {
            try? FileManager.default.moveItem(atPath: PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), toPath: PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: downloadData?.realFileExtensionType))
            fileExtensionType = (downloadData?.realFileExtensionType)!
        }

        // mdf by ak 当下载的视频 FPS 不是30帧时 要处理到30 FPS
        if downloadData?.fileExtensionType == .mp4, AVURLAsset(url: URL(fileURLWithPath: PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType)), options: avAssertOptions).tracks(withMediaType: .video).first?.nominalFrameRate != 30 {
            let oldVideoPath = PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType)

            let outVideoPath = downloadDirectory + "export_temp" + oldVideoPath.replacingOccurrences(of: downloadDirectory, with: "")

            BFLog(message: "下载视频 FPS 不是30帧 要处理 oldVideoPath is \(oldVideoPath) \n outVideoPath is\(outVideoPath)")

            NXAVAssetExportSession().exportAsynchronouslyWithCompletionHandler(inFilePath: oldVideoPath, outFilePath: outVideoPath, frameDuration: CMTime(value: 1, timescale: 30)) { _ in

                do {
                    // 1,delete old file
                    try FileManager.default.removeItem(at: URL(fileURLWithPath: oldVideoPath))
                    // 2,用新文件覆盖老文件路径
                    try FileManager.default.moveItem(atPath: outVideoPath, toPath: oldVideoPath)

                } catch {
                    // No-op
                }

                BFLog(message: "clear data movie outFilePath is \(String(describing: oldVideoPath))")

                postNotification(name: cDownloadMatrialSuccessKey, userInfo: ["code": error != nil ? "0" : "1", "url": task.taskUrl, "localPath": error != nil ? "" : PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), "fileExtensionType": fileExtensionType])
                if downloadData?.stateHandle != nil {
                    if error != nil {
                        downloadData?.state = .error
                        downloadData?.stateHandle!(.error, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), PQError(msg: error?.localizedDescription, code: 0))
                    } else {
                        downloadData?.state = .compelte
                        downloadData?.stateHandle!(.compelte, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), nil)
                    }
                }
            }

        } else {
            postNotification(name: cDownloadMatrialSuccessKey, userInfo: ["code": error != nil ? "0" : "1", "url": task.taskUrl, "localPath": error != nil ? "" : PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), "fileExtensionType": fileExtensionType])
            if downloadData?.stateHandle != nil {
                if error != nil {
                    downloadData?.state = .error
                    downloadData?.stateHandle!(.error, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), PQError(msg: error?.localizedDescription, code: 0))
                } else {
                    downloadData?.state = .compelte
                    downloadData?.stateHandle!(.compelte, taskUrl, PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: fileExtensionType), nil)
                }
            }
        }
    }
}
