//
//  MP_AVURLAsset.swift
//  MusicPlayer
//
//  Created by Mr.Zhou on 2024/5/23.
//

import UIKit
import AVFoundation
import MobileCoreServices
///自定义播放媒体
class MP_AVPlayerItem: AVPlayerItem {
    
    override init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?) {
        super.init(asset: asset, automaticallyLoadedAssetKeys: automaticallyLoadedAssetKeys)
        //禁止空间播放
        if #available(iOS 13.0, *) {
            self.isAudioSpatializationAllowed = false
        } else {
            // Fallback on earlier versions
        }
    }
}

///自定义媒体资产
class MP_AVURLAsset: AVURLAsset {
    //加载器
    private var playerResourceLoader:MP_PlayerResourceLoader?
    //标题
    private var title:String
    //加载队列
    private var assetQueue:DispatchQueue!
    ///加载网络媒体资源
    init(_ url:URL, videoId:String, title:String) {
        self.title = title
        var customURL:URL!
        //判断这个文件是否存在
        if let audioPath = MP_CacheAndArchiverManager.shared.zhoujunfeng_getCachePath(videoId) {
            //存在，使用缓存文件播放
            customURL = .init(fileURLWithPath: audioPath)
            print("从缓存中播放\(title)")
            super.init(url: customURL, options: nil)
        }else {
            //不存在，使用网络加载器加载
            customURL = MP_PlayerToolConfig.customURL(url)!
            //创建对应的加载队列
            assetQueue = .init(label: "com.relax.offline.mp3.\(videoId)")
            playerResourceLoader = .init(videoId: videoId, url: customURL, title: title)
            print("从网络媒体中播放\(title)")
            super.init(url: customURL, options: nil)
            //将系统加载器切换为自定义加载器
            self.resourceLoader.setDelegate(playerResourceLoader, queue: .global(qos: .background))
        }
        //实现预加载
        preloading()
    }
    ///加载本地媒体资源
    init(LocalURL url: URL, videoId:String, title:String) {
        self.title = title
        super.init(url: url, options: nil)
        //本地加载，不需要调用加载器
        //实现预加载
        preloading()
    }
    deinit{
        //销毁内容
        playerResourceLoader = nil
        assetQueue = nil
    }
    //预加载
    private func preloading() {
        //对该Asset实现预加载，以让Asset触发resourceLoaderdelegate
        //加载关键的播放属性
        let keys = ["playable"]
        self.loadValuesAsynchronously(forKeys: keys) {
            [weak self] in
            guard let self = self else {return}
            // 检查加载属性的结果
            for key in keys {
                var error: NSError?
                let status = statusOfValue(forKey: key, error: &error)
                if status == .loaded {
                    print("开始对\(title)的预加载")
                } else {
                    // 处理加载失败的情况
                    print("无法加载 \(key): \(String(describing: error))")
                    //一般是网络数据问题，需要重新请求一遍
                }
            }
        }
    }
}
///媒体资源加载代理
protocol MP_PlayerResourceLoaderDelegate: NSObject {
    ///检测当前加载器是否缓存完毕
    func loader(_ loader:MP_PlayerResourceLoader, isCached:Bool)
    ///检测当前加载器是否请求错误
    func loader(_ loader:MP_PlayerResourceLoader, requestError errorCode:Int)
}
typealias CheckStatusBlock = (Int) -> Void

///媒体资源加载器
class MP_PlayerResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
    ///传递来的videoId
    private var videoId:String
    ///传递来的标题
    private var title:String
    ///当前的LoadRequest数组
    private var requestListes:[AVAssetResourceLoadingRequest]!
    ///检索状态码闭包
    var checkStatusBlock:CheckStatusBlock?
    //初始化方法
    init(videoId key:String, url:URL, title:String) {
        self.videoId = key
        self.title = title
        super.init()
        //添加监听
        NotificationCenter.notificationKey.add(observer: self, selector: #selector(requestManagerDidReceiveResponseAction(_ :)), notificationName: .asset_response)
        NotificationCenter.notificationKey.add(observer: self, selector: #selector(requestManagerDidReceiveDataAction(_ :)), notificationName: .asset_receiveData)
        //实现代理
//        MP_PlayerTaskManager.shared.delegate = self
        //检索当前请求组数量，如果有，就全部移除，避免重复生成/请求
        if requestListes == nil {
            requestListes = []
        }
        
    }
    deinit {
        NotificationCenter.default.removeObserver(self)
        //移除对应的任务
        MP_PlayerTaskManager.shared.removeTask(videoId, title: title)
    }
    //MARK: - AVAssetResourceLoaderDelegate
    ///拦截系统的网络请求
    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        addLoadingRequest(loadingRequest)
        return true
    }
    //取消系统的加载请求
    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) {
        MP_PlayerTaskManager.shared.accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            guard let index = requestListes.firstIndex(where: { $0 == loadingRequest }) else {
                return
            }
            // 移除找到的请求
            requestListes.remove(at: index)
        }
    }
    //MARK: - 处理系统的LoadingRequest
    ///添加LoadingRequest
    private func addLoadingRequest(_ loadingRequest:AVAssetResourceLoadingRequest) {
        MP_PlayerTaskManager.shared.accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            requestListes.append(loadingRequest)
            //获取当前请求的请求范围
            let offset = loadingRequest.dataRequest?.requestedOffset ?? 0
            //判断cacheLengths
            if let cacheLength = MP_PlayerTaskManager.shared.cacheLengths[videoId] {
                //获得已缓存长度
                if (offset >= 0 && offset <= cacheLength) {
                    //已缓存数据超过请求范围，直接执行数据补全
                    processRequestList(videoId)
                }else {
                    //数据还没缓存到请求范围，等待数据下载
                    newTaskWithLoadingRequest(loadingRequest)
                }
            }else {
                //数据还未缓存，请求缓存
                newTaskWithLoadingRequest(loadingRequest)
            }
        }
        
    }
    ///实现互斥锁
    private func synchronized(_ lock: Any, closure: () -> Void) {
        objc_sync_enter(lock)
        closure()
        objc_sync_exit(lock)
    }
    ///实现请求任务下载
    private func newTaskWithLoadingRequest(_ loadingRequest:AVAssetResourceLoadingRequest) {
//        var fileLength:Int64 = 0
//        //获取资源长度
//        if let length = MP_PlayerTaskManager.shared.fileLengths[videoId] {
//            fileLength = length
////            MP_PlayerTaskManager.shared.cancels[videoId] = true
//        }
        guard let requestUrl = loadingRequest.request.url else {return}
        //创建请求任务
        MP_PlayerTaskManager.shared.resumeRequestStart(requestUrl, videoId: videoId, title: title)
    }
    ///处理请求组
    private func processRequestList(_ videoId:String) {
        MP_PlayerTaskManager.shared.accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            if self.videoId == videoId {
                var finishRequestList:[AVAssetResourceLoadingRequest] = []
                //是当前videoId，操作请求组
                requestListes.forEach { loadingRequest in
                    if self.finishLoadingWithLoadingRequest(loadingRequest, videoId: videoId) {
                        finishRequestList.append(loadingRequest)
                    }
                }
                //将所有完成的请求移除
                self.requestListes = requestListes.filter({!finishRequestList.contains($0)})
            }
        }
    }
    ///检索是否请求完成
    private func finishLoadingWithLoadingRequest(_ loadingRequest: AVAssetResourceLoadingRequest, videoId:String) -> Bool {
//        print("处理\(videoId)的请求信息")
        if let contentInformationRequest = loadingRequest.contentInformationRequest {
            //设置contentInformationRequest
            if let contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (MP_PlayerTaskManager.shared.mimeTypes[videoId] ?? "") as CFString, nil)?.takeRetainedValue() {
                contentInformationRequest.contentType = contentType as String
            }
            if let length = MP_PlayerTaskManager.shared.fileLengths[videoId] {
                contentInformationRequest.contentLength = length
            }
            contentInformationRequest.isByteRangeAccessSupported = true
        }
        //读取文件，填充数据
        if let cacheLength = MP_PlayerTaskManager.shared.cacheLengths[videoId], let dataRequest = loadingRequest.dataRequest{
            var requestedOffset = dataRequest.requestedOffset
            if (dataRequest.currentOffset != 0) {
                //如果当前下载长度不为0
                requestedOffset = dataRequest.currentOffset
            }
            //当前下载长度-要请求的长度
            let canReadLength = cacheLength - requestedOffset
            let respondLength = min(Int(canReadLength), (dataRequest.requestedLength))
            guard let data = MP_CacheAndArchiverManager.shared.readTempFileDataWithOffset(UInt64(requestedOffset), length: respondLength, videoId: videoId) else {return false}
            //对请求加载数据
            dataRequest.respond(with: data)
            //如果完全响应了所需要的数据，则完成
            let nowendOffset = requestedOffset + Int64(respondLength)
            let reqEndOffset = (dataRequest.requestedOffset)+Int64(dataRequest.requestedLength)
            if nowendOffset >= reqEndOffset {
                loadingRequest.finishLoading()
                return true
            }
        }else {
            print("\(videoId)没有加载数据")
        }
        return false
    }
    //MARK: - MP_PlayerTaskDelegate
    //得到服务器响应
    @objc private func requestManagerDidReceiveResponseAction(_ notification:Notification) {
        MP_PlayerTaskManager.shared.accessQueue.async {
            [weak self] in
            guard let self = self, let object = notification.object as? [String:Any] else {return}
            let id = object["videoId"] as? String
            let code = object["statusCode"] as? Int
            if self.videoId == id {
                if let block = checkStatusBlock {
                    block(code ?? 0)
                }
            }
        }
    }
    //从服务器接收到数据
    @objc private func requestManagerDidReceiveDataAction(_ notification:Notification){
        MP_PlayerTaskManager.shared.accessQueue.async {
            [weak self] in
            guard let self = self, let object = notification.object as? [String:Any] else {return}
            let id = object["videoId"] as? String
            processRequestList(id ?? "")
        }
    }
    /// 缓存结果
    func requestManagerIsCached(_ isCached: Bool, videoId:String){
        if self.videoId == videoId {
            
        }
    }
    /// 接收数据完成
    func requestManagerDidComplete(withError errorCode: Int, videoId:String){
        if self.videoId == videoId {
            
        }
    }

}


///媒体播放器工具配置
class MP_PlayerToolConfig:NSObject {
    ///自定义URL链接
    static func customURL(_ url: URL) -> URL? {
        var urlString = url.absoluteString
        if let range = urlString.range(of: ":") {
            let scheme = String(urlString[..<range.lowerBound])
            let newScheme = scheme + "-streaming"
            urlString = urlString.replacingOccurrences(of: scheme, with: newScheme)
            return URL(string: urlString)
        } else {
            return nil
        }
    }
    ///原始URL链接
    static func originalURL(_ url: URL) -> URL? {
        guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil }
        components.scheme = components.scheme?.replacingOccurrences(of: "-streaming", with: "")
        return components.url
    }
}

class MP_PlayerTaskManager:NSObject, URLSessionDataDelegate{
    static var shared = MP_PlayerTaskManager()
    ///请求代理
//    weak var delegate:MP_PlayerTaskDelegate?
    //默认的下载队列
    var accessQueue:DispatchQueue = .init(label: "com.relax.offline.mp3.accessQueue")
    //会话Session
    var session:URLSession!
    //路径组
    var urls:[String:URL]!
    //请求组
    var requests:[String:URLRequest]!
    //任务组
    var dataTasks:[String:URLSessionDataTask]!
    //响应体组
    var responses:[String:URLResponse]!
    //资源长度组
    var fileLengths:[String:Int64]!
    //已缓冲的长度组
    var cacheLengths:[String:Int64]!
    //资源类型组
    var mimeTypes:[String:String]!
    //是否取消组
    var cancels:[String:Bool]!
    //保存的下载进度组
    var mediaDatas:[String:Data]!
    //标题组
    var titles:[String:String]!
    //后台加载执行任务
    var sessionCompletionHandler: (() -> Void)?
    //默认任务最大数
    private var maxCount = 4
    override init() {
        super.init()
        //实现会话
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.relax.offline.mp3.PlayerTasksession")
        //设置回话超时时间
        configuration.timeoutIntervalForRequest = 30
        configuration.timeoutIntervalForResource = 30
        configuration.allowsCellularAccess = true
        //实现会话
        session = .init(configuration: configuration, delegate: self, delegateQueue: nil)
        //初始化
        urls = [:]
        requests = [:]
        dataTasks = [:]
        responses = [:]
        fileLengths = [:]
        cacheLengths = [:]
        mimeTypes = [:]
        cancels = [:]
        mediaDatas = [:]
        titles = [:]
    }
    //创建网络请求
    func resumeRequestStart(_ customURL:URL, videoId:String, title:String) {
        accessQueue.async {
            [weak self] in
            guard let self = self, let url = MP_PlayerToolConfig.originalURL(customURL) else {return}
            //判断管理器是否已经持有这个请求
            if urls[videoId] == nil {
                urls[videoId] = url
            }
            if titles[videoId] == nil {
                titles[videoId] = title
            }
            //创建请求
            let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 50)
            if requests[videoId] == nil {
                requests[videoId] = request
            }
            //执行请求
            requestDataTask(request, videoId: videoId, title: title)
        }
    }
    //请求任务
    private func requestDataTask(_ request:URLRequest, videoId:String, title:String) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            guard dataTasks[videoId] == nil else {return}
            //判断当前任务组中是否达到最大值
//            if dataTasks.count >= maxCount {
//                //达到或超出了，移除第一个任务
//                if let first = dataTasks.first {
////                    first.value.cancel()
//                    let firstVideoID = first.key
//                    let firstTitle = titles[firstVideoID] ?? ""
//                    //移除其他相关设置
//                    removeTask(firstVideoID, title: firstTitle)
//                }
//            }
            //创建一个临时文件
            let _ = MP_CacheAndArchiverManager.shared.createTempFile(videoId)
            //创建任务
            let dataTask = session.dataTask(with: request)
            dataTask.priority = URLSessionTask.highPriority
            dataTasks[videoId] = dataTask
            dataTask.resume()
        }
    }
    //MARK: - URLSessionDataDelegate
    //服务器响应
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            //根据任务获取对应的VideoId
            guard let videoId = dataTasks.first(where: {$0.value == dataTask})?.key, cancels[videoId] != true else {return}
//            print("\(videoId)已连接到服务器")
            if responses[videoId] == nil {
                //更新响应头
                responses[videoId] = response
            }
            guard let httpResponse = response as? HTTPURLResponse else {return}
            //获取状态码
            let statusCode = httpResponse.statusCode
            if statusCode == 200 {
                //成功获取到数据
                if let contentLength = httpResponse.allHeaderFields["Content-Length"] as? String, let contentLengthValue = Int64(contentLength) {
                    //跟新对应文件长度
                    fileLengths[videoId] = contentLengthValue > 0 ? contentLengthValue : response.expectedContentLength
                } else {
                    fileLengths[videoId] = response.expectedContentLength
                }
                //更新文件类型
                if let contentType = httpResponse.allHeaderFields["Content-Type"] as? String {
                    mimeTypes[videoId] = contentType
                }else {
                    mimeTypes[videoId] = response.mimeType
                }
            }else if (statusCode == 206) {///携带Range，不需要处理
//                if let contentLength = httpResponse.allHeaderFields["Content-Length"] as? String, let contentLengthValue = Int64(contentLength) {
//                    //跟新对应文件长度
//                    fileLengths[videoId] = contentLengthValue > 0 ? contentLengthValue : response.expectedContentLength
//                } else {
//                    fileLengths[videoId] = response.expectedContentLength
//                }
//                //更新文件类型
//                if let contentType = httpResponse.allHeaderFields["Content-Type"] as? String {
//                    mimeTypes[videoId] = contentType
//                }else {
//                    mimeTypes[videoId] = response.mimeType
//                }
            }else if (statusCode == 403){//访问被拒绝了，通常是用户执行了切换IP的操作，导致油管服务器不准访问（IP限制）
                print("响应码403-\(titles[videoId] ?? "")将重新请求资源")
                //使用videoId重新获取权限数据
                switchURLAction(task: dataTask, videoId: videoId)
            }else if (statusCode == 416) {//超出文件大小，手动重试
                print("\(titles[videoId] ?? "")请求范围超出文件大小")
                retryTask(task: dataTask)
            }else {
                print("\(titles[videoId] ?? "")响应出错了，任务取消，状态码为\(statusCode)")
            }
            //执行代理
            NotificationCenter.notificationKey.post(notificationName: .asset_response, object: ["statusCode":statusCode,"videoId":videoId])
//            self.delegate?.requestManagerDidReceiveResponse(withStatusCode: statusCode, videoId: videoId)
            //允许继续响应
            completionHandler(.allow)
        }
    }
    //服务器返回数据（多次调用）
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            guard let videoId = dataTasks.first(where: {$0.value == dataTask})?.key, cancels[videoId] != true else {return}
            //获取相应到的数据
            let currentLength = self.cacheLengths[videoId] ?? 0
            self.cacheLengths[videoId] = currentLength + Int64(data.count)
            //根据videoId，写入相应的临时缓冲文件
            MP_CacheAndArchiverManager.shared.writeDataToAudioFileTempPathWithData(data, videoId: videoId)
            //回调下载事件代理
//            self.delegate?.requestManagerDidReceiveData(videoId)
            NotificationCenter.notificationKey.post(notificationName: .asset_receiveData, object: ["videoId":videoId])
        }
    }
    //请求完成会调用该方法，请求失败则error有值
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: (any Error)?) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            guard let videoId = dataTasks.first(where: {$0.value == task})?.key, cancels[videoId] != true, let title = titles[videoId] else {return}
            //检索是否存在报错
            if let error = error {
                //触发报错，打印报错内容，并重新调用
                print("加载\(title)报错，错误详情:\(error.localizedDescription)")
                //处理错误
                if let urlError = error as? URLError {
                    switch urlError.code {
                    case .notConnectedToInternet://网络连接错误
                        print("网络连接错误")
                    case .timedOut://链接超时
                        print("\(title)链接超时")
                        //启用重试机制
                        retryTask(task: task)
                    case .cannotConnectToHost://服务器未响应
                        print("\(title)链接服务器未响应")
                        //启用重试机制
                        retryTask(task: task)
                    case .secureConnectionFailed://证书失效
                        print("\(title)链接证书失效")
                        //证书失效了
                        retryTask(task: task)
                    case .networkConnectionLost://链接中断
                        print("\(title)加载中断了")
                        //启用重试机制
                        retryTask(task: task)
                    case .cancelled:
                        print("\(title)加载取消了")
                        //启用重试机制
                        retryTask(task: task)
                    default:
                        break
                    }
                }
            }else {
                //没有报错，下载完成
                _ = MP_CacheAndArchiverManager.shared.moveAudioFileFromTempPathToCachePath(videoId)
                NotificationCenter.notificationKey.post(notificationName: .asset_isCached, object: ["videoId":videoId])
            }
        }
    }
    //重试机制
    private func retryTask(task:URLSessionTask) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            //获取videoID
            guard let videoId = dataTasks.first(where: {$0.value == task})?.key, let title = titles[videoId] else {return}
            // 根据需要设定重试延时，例如使用指数退避策略
            let retryDelay:Double = 3 // 每次延时3秒
            accessQueue.asyncAfter(deadline: .now() + retryDelay) {
                [weak self] in
                //尝试重试
                print("\(title)尝试重试")
                guard let self = self, var request = requests[videoId] else {return}
                //假如cacheLength有值，那说明是断点传续
                if let cacheLength = cacheLengths[videoId] {
                    var range:String!
                    if let filePathLength = fileLengths[videoId] {
                        if cacheLength < filePathLength {
                            //计算最后的值
                            range = "bytes=\(cacheLength)-\(filePathLength-1)"
                            request.setValue(range, forHTTPHeaderField: "Range")
                        }
                    }else {
                        range = "bytes=\(cacheLength)-"
                        //创建任务
                        request.setValue(range, forHTTPHeaderField: "Range")
                    }
                }
                let dataTask = session.dataTask(with: request)
                requests[videoId] = request
                dataTasks[videoId] = dataTask
                dataTask.resume()
            }
        }
    }
    //403事件切换URL
    private func switchURLAction(task:URLSessionTask, videoId:String) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            task.cancel()
            MP_NetWorkManager.shared.requestAndroidPlayer(videoId, playlistId: "") { [weak self] results, cs in
                //只需要获取results第一位,也就是新的资源路径
                guard let self = self, let first = results?.0.first else {return}
                //通知更新
                NotificationCenter.notificationKey.post(notificationName: .player_asset_403, object: first)
                guard let url = URL(string: first) else {return}
                accessQueue.async {
                    [weak self] in
                    guard let self = self else {return}
                    //取消原来的任务
                    if dataTasks[videoId] != nil {
                        dataTasks[videoId] = nil
                    }
                    //成功创建了新的资源路径,创建新的请求
                    var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 50)
                    //假如cacheLength有值，那说明是断点传续
                    if let cacheLength = cacheLengths[videoId] {
                        var range:String!
                        if let filePathLength = fileLengths[videoId] {
                            if cacheLength < filePathLength {
                                //计算最后的值
                                range = "bytes=\(cacheLength)-\(filePathLength-1)"
                                request.setValue(range, forHTTPHeaderField: "Range")
                            }
                        }else {
                            range = "bytes=\(cacheLength)-"
                            //创建任务
                            request.setValue(range, forHTTPHeaderField: "Range")
                        }
                    }
                    //创建新的任务
                    let dataTask = session.dataTask(with: request)
                    requests[videoId] = request
                    dataTasks[videoId] = dataTask
                    dataTask.resume()
                }
            }
        }
    }
    //下载任务移除
    func removeTask(_ videoId:String, title:String) {
        accessQueue.async {
            [weak self] in
            guard let self = self else {return}
            urls[videoId] = nil
            requests[videoId] = nil
            dataTasks[videoId]?.cancel()
            dataTasks[videoId] = nil
            responses[videoId] = nil
            fileLengths[videoId] = nil
            cacheLengths[videoId] = nil
            mimeTypes[videoId] = nil
            cancels[videoId] = nil
            print("移除了\(title)")
        }
    }
}
