//
//  MP_DownloadManager.swift
//  MusicPlayer
//
//  Created by 忆海16 on 2024/5/14.
//

import Foundation
import Tiercel
import DownloadButton

enum MP_DownloadTaskStatus {
    ///处于队列中
    case queued
    ///下载中
    case downloading
    ///下载完成
    case finished
    ///需要重新下载
    case failed
}
///下载管理器
class MP_DownloadManager: NSObject {
    static let shared = MP_DownloadManager()
    //文件管理器
    private let fileManager = FileManager.default
    //沙盒文件
    private let DocumentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    ///会话队列
    var loadQueue:DispatchQueue = .init(label: "com.relax.offline.mp3.loadQueue", qos: .background)
    //下载任务会话
    var session: SessionManager!
    //下载链接组
    private var downloadURLs:[String: URL] = [:]
    //待下载任务队列组
    private var downloadTasks: [String: (task: DownloadTask, status: MP_DownloadTaskStatus)] = [:]
    //活跃的下载任务队列组
    private var queuedTaskVideoIds: [String] = []
    // 用于存储每个任务的 backgroundTaskID
    private var backgroundTaskIDs: [String: UIBackgroundTaskIdentifier] = [:]
    //下载任务进度组
    private var progressStorage: [String: CGFloat] = [:] // 新增进度存储
    //音乐信息组
    private var songHandlers:[String: MPPositive_SongItemModel] = [:]
    //下载文件夹名字
    private var downloadDocumentName:String = "Downloads"
    // 添加一个标志位来控制循环结束
    private var isDownloading: Bool = false
    //最大同时活跃下载任务数
    private var maxTasksCount:Int = 6

    private override init() {
        super.init()
        var configuration = SessionConfiguration()
        configuration.timeoutIntervalForRequest = 60
        configuration.maxConcurrentTasksLimit = maxTasksCount
        configuration.allowsCellularAccess = true
        
        //设置会话
        session = SessionManager("com.relax.offline.mp3.music.backgroundDownload", configuration: configuration, operationQueue: loadQueue)
        //创建下载文件夹
        let downloadsURL = DocumentsURL.appendingPathComponent(downloadDocumentName)
        //检索下载文件夹是否存在
        if !fileManager.fileExists(atPath: downloadsURL.path) {
            //不存在，新建下载文件夹
            do{
                //新建下载文件夹
                try fileManager.createDirectory(at: downloadsURL, withIntermediateDirectories: true, attributes: nil)
            }catch {
                print("创建下载文件夹失败, 失败原因:\(error.localizedDescription)")
            }
        }
    }
    ///任务准备
    func prepareVideoDownloadTask(from song:MPPositive_SongItemModel){
        //根据歌曲信息获得下载链接，并创建下载任务(允许压缩下载)
        guard let url = URL(string: song.resourceUrls?.first ?? ""), let videoId = song.videoId else {
            //获取链接失败
            MP_HUD.error("Source details not available yet, please try again later!", delay: 2.0, completion: nil)
            //失败事件埋点
            MP_AnalyticsManager.shared.player_b_downloadfailure_errorAction(song.videoId ?? "", videoname: song.title ?? "", artistname: song.shortBylineText ?? "", error: "Failed to create download link")
            return
        }
        if url.scheme == "file" {
            print("用户对同一首歌删除又下载")
            //出现这种情况是用户在播放器界面移除了下载歌曲，又继续点了下载同一首歌，当前歌曲资源的并未刷新，提供的路径资源仍旧是下载后的本地资源，需要重新调用网络请求获取
            MP_NetWorkManager.shared.requestNextList("", videoId: videoId, clickTrackingParams: nil){
                [weak self] listSongs in
                guard let self = self, let first = listSongs.first else {
                    return
                }
                let group = DispatchGroup()
                group.enter()
                improveDataforLycirsAndRelated(first) {(result)  in
                    first.lyricsID = result.0
                    first.relatedID = result.1
                    group.leave()
                }
                group.enter()
                //补全资源路径组和封面路径组
                improveDataforResouceAndCover(first) {(resourceUrls, coverUrls) in
                    if let resourceUrls = resourceUrls {
                        first.resourceUrls = resourceUrls.0
                        first.itags = resourceUrls.1
                        first.mimeTypes = resourceUrls.2
                    }
                    first.coverUrls = coverUrls
                    group.leave()
                }
                group.notify(queue: .main, execute: {
                    [weak self] in
                    guard let self = self else {
                        return
                    }
                    //补全了数据,执行下载
                    prepareVideoDownloadTask(from: first)
                })
            }
            return
        }
        //将歌曲模型加入歌曲组
        songHandlers[videoId] = song
        //将下载链接加入链接组
        downloadURLs[videoId] = url
        queuedTaskVideoIds.append(videoId)
        //执行下载任务
        executeVideoDownloadTask()
    }
    //执行下载任务
    private func executeVideoDownloadTask() {
        print("当前运行的任务数量: \(session.tasks.filter({ $0.status == .running }).count)")
        print("待下载队列: \(queuedTaskVideoIds)")
        while session.tasks.filter({ $0.status == .running }).count < maxTasksCount, let nextVideoId = queuedTaskVideoIds.first, let url = downloadURLs[nextVideoId], let downloadTask = session.download(url, headers: ["Accept-Encoding": "gzip, deflate"], onMainQueue: false) {
            queuedTaskVideoIds.removeFirst()
            //将下载任务加入任务队列
            downloadTasks[nextVideoId] = (downloadTask, .queued)
            //开始执行任务
            if let taskData = downloadTasks[nextVideoId], taskData.status == .queued {
                let task = taskData.task
                var bgTaskID: UIBackgroundTaskIdentifier = .invalid
                bgTaskID = UIApplication.shared.beginBackgroundTask(withName: "DownloadTask-\(nextVideoId)") {
                    UIApplication.shared.endBackgroundTask(bgTaskID)
                    self.backgroundTaskIDs[nextVideoId] = .invalid
                }
                self.backgroundTaskIDs[nextVideoId] = bgTaskID
                //下载任务进度值回调
                task.progress { [weak self] (task) in
                    guard let self = self else {return}
                    if self.downloadTasks[nextVideoId]?.status == .queued {
                        self.downloadTasks[nextVideoId]?.status = .downloading
                    }
                    //更新对应任务（videoId）的进度值
                    progressStorage[nextVideoId] = task.progress.fractionCompleted
                    //执行下载进度更新代理
                    NotificationCenter.notificationKey.post(notificationName: .download_progress_source, object: ["videoId":nextVideoId])
                }
                //下载任务完成状态回调
                task.success {[weak self] (task) in
                    //下载成功
                    guard let self = self else {return}
                    //下载任务缓存地址
                    let filePathUrl:URL = URL(fileURLWithPath: task.filePath)
                    print("任务下载地址:\(filePathUrl.path)")
                    //获取下载文件夹地址
                    let downloadsURL = DocumentsURL.appendingPathComponent(downloadDocumentName)
                    //创建真实下载地址
                    let fileURL = downloadsURL.appendingPathComponent("\(nextVideoId).mp4")
                    //判断真实下载地址是否存在对应文件
                    if fileManager.fileExists(atPath: fileURL.path) {
                        //确实存在一个文件，将它删除
                        try? fileManager.removeItem(at: fileURL)
                    }
                    //将任务缓存文件移动到真实下载地址
                    do{
                        try fileManager.moveItem(at: filePathUrl, to: fileURL)
                    }catch{
                        //移动失败，终止任务
                        NotificationCenter.notificationKey.post(notificationName: .dowload_end_source, object: ["videoId":nextVideoId])
                        //发布报错埋点
                        MP_AnalyticsManager.shared.player_b_downloadfailure_errorAction(nextVideoId, videoname: songHandlers[nextVideoId]?.title ?? "", artistname: songHandlers[nextVideoId]?.shortBylineText ?? "", error: error.localizedDescription)
                        //清除已失败任务的队列组内容
                        if self.downloadTasks[nextVideoId]?.status == .downloading {
                            self.downloadTasks[nextVideoId]?.status = .failed
                        }
                        downloadURLs[nextVideoId] = nil
                        progressStorage[nextVideoId] = nil
                        songHandlers[nextVideoId] = nil
                        MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil)
                        session.cancel(task) { _ in
                            self.downloadTasks[nextVideoId] = nil
                            print("移动\(self.songHandlers[nextVideoId]?.title ?? "")到真实下载地址失败，失败原因:\(error)")
                        }
                        //启动新的任务
                        self.executeVideoDownloadTask()
                        return
                    }
                    //文件移动完成后，执行任务队列清理
                    saveLoadVideoItem(self.songHandlers[nextVideoId]!){
                        [weak self] in
                        NotificationCenter.notificationKey.post(notificationName: .dowload_end_source, object: ["videoId":nextVideoId])
                    }
                    // 清除已完成任务的进度记录
                    if self.downloadTasks[nextVideoId]?.status == .downloading {
                        self.downloadTasks[nextVideoId]?.status = .finished
                    }
                    self.downloadURLs[nextVideoId] = nil
                    self.downloadTasks[nextVideoId] = nil
                    self.progressStorage[nextVideoId] = nil
                    self.songHandlers[nextVideoId] = nil
                    //告知用户下载成功
                    MP_HUD.downloadText("Download Successfull", delay: 1.0, completion: nil)
                    //结束后台任务
                    if let bgTaskID = self.backgroundTaskIDs[nextVideoId] {
                        UIApplication.shared.endBackgroundTask(bgTaskID)
                        self.backgroundTaskIDs[nextVideoId] = .invalid
                    }
                    session.remove(task)
                    //启动新的任务
                    self.executeVideoDownloadTask()
                }.failure { [weak self] task in
                    //下载失败
                    guard let self = self else {return}
                    //确定任务报错是否存在
                    if let error = task.error{
                        //任务报错存在,处理完成状态回调
                        NotificationCenter.notificationKey.post(notificationName: .dowload_end_source, object: ["videoId":nextVideoId])
                        //发布报错埋点
                        MP_AnalyticsManager.shared.player_b_downloadfailure_errorAction(nextVideoId, videoname: songHandlers[nextVideoId]?.title ?? "", artistname: songHandlers[nextVideoId]?.shortBylineText ?? "", error: error.localizedDescription)
                        //清除已失败任务的队列组内容
                        if self.downloadTasks[nextVideoId]?.status == .downloading {
                            self.downloadTasks[nextVideoId]?.status = .failed
                        }
                        downloadURLs[nextVideoId] = nil
                        progressStorage[nextVideoId] = nil
                        songHandlers[nextVideoId] = nil
                        switch error.localizedDescription {
                        case "The operation couldn’t be completed. No space left on device":
                            MP_HUD.text("Insufficient storage space, download failed", delay: 1.5, completion: nil)
                        default:
                            MP_HUD.text("An error occurred while downloading. Please download again.", delay: 1.5, completion: nil)
                        }
                        session.cancel(task) { _ in
                            self.downloadTasks[nextVideoId] = nil
                            print("\(self.songHandlers[nextVideoId]?.title ?? "")下载任务失败，失败原因:\(error)")
                            //结束后台任务
                            if let bgTaskID = self.backgroundTaskIDs[nextVideoId] {
                                UIApplication.shared.endBackgroundTask(bgTaskID)
                                self.backgroundTaskIDs[nextVideoId] = .invalid
                            }
                        }
                        //启动新的任务
                        self.executeVideoDownloadTask()
                    }
                }
            }
        }
    }
    ///当前音乐是否存在于下载任务队列中
    func isTasksQueue(for videoId:String) -> Bool {
        if let _ = songHandlers[videoId] {
            return true
        }
        return false
    }
    ///判断是否活跃下载任务
    func isActiveTask(for videoId:String) -> Bool {
        if let taskData = downloadTasks[videoId] {
            return taskData.status == .downloading
        }
        return false
    }
    ///获取当前音乐任务下载进度
    func getProgress(for videoId: String) -> CGFloat?{
        return progressStorage[videoId]
    }
    func cancelAllTasksIfNeeded() {
        // 根据需求，取消所有任务，或者根据任务状态进行过滤
        for key in progressStorage.keys {
            session.cancel(key)
        }
    }
    ///检索文件是否下载
    func isDownloadedFileDocuments(_ videoId:String, completion: @escaping (Bool) -> Void){
        //优先检索数据库模型是否存在
        MPPositive_DownloadItemModel.fetch(predicate: .init(format: "videoId == %@", videoId)) { [weak self] results in
            if results.isEmpty {
                // 数据库中没有记录，直接返回 false
                DispatchQueue.main.async {
                    completion(false)
                }
                return
            }
            //数据库中存在数据
            DispatchQueue.global(qos: .userInitiated).async {
                let fileURL = self?.getDocumentsFileURL(videoId)
                // 回到主线程调用回调闭包
                DispatchQueue.main.async {
                    completion(fileURL != nil)
                }
            }
        }
    }
    ///获取沙盒下载路径
    func getDocumentsFileURL(_ videoID: String) -> String? {
        // 获取Documents目录的URL
        let documentsDirectoryURL = DocumentsURL.appendingPathComponent("Downloads")
        // 根据videoId构建文件完整URL
        let fileURL = documentsDirectoryURL.appendingPathComponent("\(videoID).mp4")
        //检索是否存在
        if FileManager.default.fileExists(atPath: fileURL.path) == true {
            //存在
            return fileURL.absoluteString
        }else {
            return nil
        }
    }
    ///取消下载
    func cancelDownloadTask(_ videoId:String, completion:((String) -> Void)?) {
        DispatchQueue.main.async {
            [weak self] in
            guard let self = self else {return}
            //发布报错埋点
            MP_AnalyticsManager.shared.player_b_downloadfailure_errorAction(videoId, videoname: songHandlers[videoId]?.title ?? "", artistname: songHandlers[videoId]?.shortBylineText ?? "", error: "User Cancel Task")
            queuedTaskVideoIds.removeAll(where: {$0 == videoId})
            //清除已失败任务的队列组内容
            if self.downloadTasks[videoId]?.status == .downloading {
                self.downloadTasks[videoId]?.status = .failed
            }
            downloadURLs[videoId] = nil
            progressStorage[videoId] = nil
            songHandlers[videoId] = nil
            if let task = self.downloadTasks[videoId]?.task {
                session.cancel(task) { _ in
                    self.downloadTasks[videoId] = nil
                    print("\(self.songHandlers[videoId]?.title ?? "")下载任务失败，失败原因:用户取消了")
                }
            }
            //结束后台任务
            if let bgTaskID = self.backgroundTaskIDs[videoId] {
                UIApplication.shared.endBackgroundTask(bgTaskID)
                self.backgroundTaskIDs[videoId] = .invalid
            }
            deleteFileDocuments(videoId, completion: nil)
            if completion != nil {
                completion!(videoId)
            }
        }
    }
    ///删除下载文件
    func deleteFileDocuments(_ videoId:String, completion:((String) -> Void)?) {
        DispatchQueue.main.async {
            [weak self] in
            guard let self = self else {return}
            //移除数据库下载模型
            MPPositive_DownloadItemModel.fetch(predicate: .init(format: "videoId == %@", videoId)) { results in
                results.forEach { item in
                    if item.videoId == videoId {
                        MPPositive_DownloadItemModel.delete(item)
                    }
                }
                MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
                let downloadsURL = self.DocumentsURL.appendingPathComponent("Downloads")
                let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4")
                if self.fileManager.fileExists(atPath: fileURL.path) {
                    do{
                        try FileManager.default.removeItem(at: fileURL)
                        //文件删除成功
                        if completion != nil {
                            completion!(videoId)
                        }
                        print("成功删除了\(videoId)文件")
                    }catch{
                        //文件删除成功
                        if completion != nil {
                            completion!(videoId)
                        }
                        print("删除文件时发生错误：\(error)")
                    }
                }else {
                    //文件删除成功
                    if completion != nil {
                        completion!(videoId)
                    }
                    print("文件不存在")
                }
            }
        }
    }
}

