//
//  VideoSimilarManager.swift
//  CleanPhoto
//
//  Created by edy on 2025/5/9.
//

import Foundation
import Photos
import UIKit
import AVFoundation

// MARK: - 缓存机制
private struct VideoAssetCache {
    let avAsset: AVAsset
    let size: Int64
    let frameRate: Double
    let firstFrame: UIImage?
}


private actor VideoAssetCacheManager {
    private var cache: [String: VideoAssetCache] = [:]
    private let maxCacheSize = 50 // 最大缓存数量
    
    func getCache(for identifier: String) -> VideoAssetCache? {
        return cache[identifier]
    }
    
    func setCache(_ videoCache: VideoAssetCache, for identifier: String) {
        // 如果缓存过大，移除最早的项
        if cache.count >= maxCacheSize {
            let oldestKey = cache.keys.first
            if let key = oldestKey {
                cache.removeValue(forKey: key)
            }
        }
        cache[identifier] = videoCache
    }
    
    func clearCache() {
        cache.removeAll()
    }
}



class VideoSimilarJSONManager: @unchecked Sendable {
    static let shared = VideoSimilarJSONManager()
    private let stateManager = PhotoSimilarStateManager()
    private init() {}

    // 类中添加缓存管理器实例
    private let assetCacheManager = VideoAssetCacheManager()
    // MARK: - 配置参数
    private let timeWindowInSeconds: TimeInterval = 600 // 10分钟时间窗口
    private let durationThreshold: Double = 0.1 // 时长相差阈值（20%）
    private let resolutionThreshold: Double = 0.2 // 分辨率相差阈值（20%）
    private let frameRateThreshold: Double = 0.1 // 帧率相差阈值（10%）
    private let hashDistanceThreshold: Int = 10 // 第一帧图像hash汉明距离阈值

    // 文件路径
    private var timeGroupsPath: String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0].appendingPathComponent("VideoTimeGroups.json").path
    }
    
    private var similarGroupsPath: String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0].appendingPathComponent("VideoSimilarGroups.json").path
    }
    
    private var currentTask: Task<Void, Error>?
    private let latestVideoTimeKey = "LatestVideoTimestamp"
    
    // MARK: - 主要处理函数
    func findSimilarVideos(in assets: [PHAsset],
                          progressHandler: (([AssetModel]) -> Void)?,
                          completionHandler: (([[AssetModel]]) -> Void)?) {
        Task {
            // 1. 加载本地数据
            await loadStoredData()
            
            // 2. 获取上次记录的最新资源时间戳
            var lastLatestTime = UserDefaults.standard.double(forKey: latestVideoTimeKey)

            if lastLatestTime == 0{
                lastLatestTime = assets.first?.creationDate?.timeIntervalSince1970 ?? 0
            }
            
            // 3. 通知已缓存的结果
            let cachedGroups = await loadSimilarGroups() //await stateManager.getAllSimilarGroups()
            await MainActor.run {
                for group in cachedGroups {
                    progressHandler?(group.assets)
                }
            }
            
            // 4. 时间分组处理
            // 拿到大于上次存储最新时间的资源
            let newAssets = assets.filter{$0.creationDate?.timeIntervalSince1970 ?? 0 > lastLatestTime}
            // 拿到最新资源时间之前的资源
            let oldAssets = assets.filter{$0.creationDate?.timeIntervalSince1970 ?? 0 <= lastLatestTime}
            
            // 更新最新资源的时间
            if let latestAsset = assets.first {
                let latestTime = latestAsset.creationDate?.timeIntervalSince1970 ?? 0
                UserDefaults.standard.set(latestTime, forKey: latestVideoTimeKey)
                UserDefaults.standard.synchronize()
            }
            
            let newtimeGroup = groupVideosByTimeWindow(newAssets)
            let oldGroups = groupVideosByTimeWindow(oldAssets)
            let timeGroups = newtimeGroup + oldGroups
            
            var unprocessedGroups: [[PHAsset]] = []
            let processedTimeGroups = await stateManager.getAllTimeGroups()
            
            // 5. 过滤已处理的时间组
            for group in timeGroups {
                if let firstAsset = group.first,
                   let lastAsset = group.last,
                   let firstDate = firstAsset.creationDate,
                   let lastDate = lastAsset.creationDate {
                    let isProcessed = processedTimeGroups.contains { timeGroup in
                        return timeGroup.startTime <= firstDate.timeIntervalSince1970 &&
                        timeGroup.endTime >= lastDate.timeIntervalSince1970 &&
                        timeGroup.isProcessed
                    }
                    if !isProcessed {
                        unprocessedGroups.append(group)
                    }
                }
            }
            
            // 6. 并发处理未处理的组
            let maxConcurrency = 3 // 视频处理较重，降低并发数
            let batchSize = max(1, unprocessedGroups.count / maxConcurrency)
            
            if unprocessedGroups.isEmpty {
                let total = cachedGroups.compactMap { $0.assets }
                completionHandler?(total)
                return
            }
            
            for batchIndex in stride(from: 0, to: unprocessedGroups.count, by: batchSize) {
                let batch = Array(unprocessedGroups[batchIndex..<min(batchIndex + batchSize, unprocessedGroups.count)])
                await withTaskGroup(of: Void.self) { group in
                    autoreleasepool {
                        for unGroup in batch {
                            group.addTask { [weak self] in
                                guard let self = self else { return }
                                
                                // 6.1 按视频特征预分组
                                let preGroups = await self.groupVideosByFeatures(unGroup)
                                
                                // 6.2 处理每个预分组
                                for preGroup in preGroups {
                                    let similarGroups = await self.findSimilarInGroupByFirstFrame(preGroup)
                                    
                                    // 6.3 保存相似组
                                    for similarGroup in similarGroups {
                                        let groupId = UUID().uuidString
                                        let assetModels = await self.createAssetModels(from: similarGroup)
                                        
                                        // 6.4 通知进度
                                        await MainActor.run {
                                            progressHandler?(assetModels)
                                        }
                                        
                                        // 6.5 保存相似组
                                        await self.stateManager.appendSimilarGroup(
                                            SimilarGroupModel(groupId: groupId, assets: assetModels)
                                        )
                                        
                                        if await self.stateManager.shouldSavePendingGroups() {
                                            await self.savePendingSimilarGroups()
                                        }
                                    }
                                }
                                
                                // 6.6 标记时间组为已处理
                                if let firstDate = unGroup.first?.creationDate,
                                   let lastDate = unGroup.last?.creationDate {
                                    let groupId = "\(Int(firstDate.timeIntervalSince1970))_\(Int(lastDate.timeIntervalSince1970))"
                                    let timeGroup = TimeGroupModel(
                                        groupId: groupId,
                                        startTime: firstDate.timeIntervalSince1970,
                                        endTime: lastDate.timeIntervalSince1970,
                                        isProcessed: true
                                    )
                                    await self.saveTimeGroup(timeGroup)
                                }
                            }
                        }
                    }
                }
            }
            
            // 7. 完成处理
            if await !stateManager.getpendingSimilarGroups().isEmpty {
                await savePendingSimilarGroups()
            }
            
            let allGroups = await loadSimilarGroups() //await stateManager.getAllSimilarGroups()
            await MainActor.run {
                completionHandler?(allGroups.map { $0.assets })
            }
        }
    }
    
    // MARK: - 视频特征分组
    private func groupVideosByFeatures(_ assets: [PHAsset]) async -> [[PHAsset]] {
        var featureGroups: [[PHAsset]] = []
        var processedAssets = Set<String>()
        
        for asset in assets {
            if processedAssets.contains(asset.localIdentifier) {
                continue
            }
            
            var currentGroup = [asset]
            processedAssets.insert(asset.localIdentifier)
            
            let assetDuration = asset.duration
            let assetSize = Double(asset.pixelWidth * asset.pixelHeight)
            
            // 获取视频帧率
            let assetFrameRate = await getVideoFrameRate(asset)
            
            for compareAsset in assets {
                if processedAssets.contains(compareAsset.localIdentifier) {
                    continue
                }
                
                // 比较时长
                let durationRatio = abs(compareAsset.duration - assetDuration) / max(compareAsset.duration, assetDuration)
                if durationRatio > durationThreshold {
                    continue
                }
                
                // 比较分辨率
                let compareSize = Double(compareAsset.pixelWidth * compareAsset.pixelHeight)
                let sizeRatio = abs(compareSize - assetSize) / max(compareSize, assetSize)
                if sizeRatio > resolutionThreshold {
                    continue
                }
                
                // 比较帧率
                let compareFrameRate = await getVideoFrameRate(compareAsset)
                let frameRateRatio = abs(compareFrameRate - assetFrameRate) / max(compareFrameRate, assetFrameRate)
                if frameRateRatio > frameRateThreshold {
                    continue
                }
                
                currentGroup.append(compareAsset)
                processedAssets.insert(compareAsset.localIdentifier)
            }
            
            if currentGroup.count > 1 {
                featureGroups.append(currentGroup)
            }
        }
        
        return featureGroups
    }
    
    private func getVideoFrameRate(_ asset: PHAsset) async -> Double {
        // 先检查缓存
        if let cache = await assetCacheManager.getCache(for: asset.localIdentifier) {
            return cache.frameRate
        }
        
        return await withCheckedContinuation { continuation in
            let options = PHVideoRequestOptions()
            options.version = .original
            options.deliveryMode = .fastFormat
            options.isNetworkAccessAllowed = false
            
            PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { [weak self] avAsset, _, _ in
                guard let self = self,
                      let videoAsset = avAsset,
                      let track = videoAsset.tracks(withMediaType: .video).first else {
                    continuation.resume(returning: 0.0)
                    return
                }
                
                // 获取第一帧
                let generator = AVAssetImageGenerator(asset: videoAsset)
                generator.appliesPreferredTrackTransform = true
                generator.maximumSize = CGSize(width: 640, height: 640)
                
                var firstFrame: UIImage?
                do {
                    let cgImage = try generator.copyCGImage(at: .zero, actualTime: nil)
                    firstFrame = UIImage(cgImage: cgImage)
                } catch {
                    print("获取第一帧失败:", error)
                }
                
                // 创建并保存缓存
                let frameRate = Double(track.nominalFrameRate)
                let cache = VideoAssetCache(
                    avAsset: videoAsset,
                    size: 0, // 这里可以根据需要获取视频大小
                    frameRate: frameRate,
                    firstFrame: firstFrame
                )
                
                Task {
                    await self.assetCacheManager.setCache(cache, for: asset.localIdentifier)
                }
                
                continuation.resume(returning: frameRate)
            }
        }
    }

    private func getFirstFrame(for asset: PHAsset) async -> UIImage? {
        // 先检查缓存
        if let cache = await assetCacheManager.getCache(for: asset.localIdentifier) {
            return cache.firstFrame
        }
        
        // 如果缓存中没有，调用 getVideoFrameRate 会自动缓存 VideoAssetCache
        _ = await getVideoFrameRate(asset)
        
        // 再次检查缓存
        if let cache = await assetCacheManager.getCache(for: asset.localIdentifier) {
            return cache.firstFrame
        }
        
        return nil
    }
    
    
    // MARK: - 第一帧相似度比较
    private func findSimilarInGroupByFirstFrame(_ assets: [PHAsset]) async -> [[PHAsset]] {
        // 1. 获取所有视频第一帧的hash
        var assetHashes: [(asset: PHAsset, hash: String)] = []
        for asset in assets {
            if let hash = await getFirstFrameHash(for: asset) {
                assetHashes.append((asset, hash))
            }
        }
        
        guard !assetHashes.isEmpty else { return [] }
        
        // 2. 将hash字符串转换为特征向量
        let vectors = assetHashes.map { hashToVector($0.hash) }
        
        // 3. 执行K-Means聚类
        let k = min(vectors.count, 10) // 限制最多10个簇，与照片管理器保持一致
        let labels = kMeansClustering(data: vectors, k: k)
        
        // 4. 将聚类结果转换为相似组
        var similarGroups: [[PHAsset]] = []
        var groupedAssets: [Int: [PHAsset]] = [:]
        
        // 根据标签将资源分组
        for (index, label) in labels.enumerated() {
            if groupedAssets[label] == nil {
                groupedAssets[label] = []
            }
            groupedAssets[label]?.append(assetHashes[index].asset)
        }
        
        // 只保留包含多个资源的组
        for (_, group) in groupedAssets {
            if group.count > 1 {
                similarGroups.append(group)
            }
        }
        
        return similarGroups
    }

    // 将hash字符串转换为特征向量
    private func hashToVector(_ hash: String) -> [Double] {
        var vector: [Double] = []
        let chunkSize = 8
        
        // 将二进制hash字符串按8位分组，转换为数值
        for i in stride(from: 0, to: hash.count, by: chunkSize) {
            let endIndex = min(i + chunkSize, hash.count)
            let start = hash.index(hash.startIndex, offsetBy: i)
            let end = hash.index(hash.startIndex, offsetBy: endIndex)
            let chunk = String(hash[start..<end])
            if let value = Int(chunk, radix: 2) {
                vector.append(Double(value))
            }
        }
        
        return vector
    }

    // K-Means 聚类算法
    func kMeansClustering(data: [[Double]], k: Int, maxIterations: Int = 100) -> [Int] {
        guard data.count > 0 && k > 0 && k <= data.count else {
            return []
        }
        
        var centroids = (0..<k).map { _ in data.randomElement()! }
        var labels = Array(repeating: 0, count: data.count)
        
        for _ in 0..<maxIterations {
            var newCentroids = Array(repeating: Array(repeating: 0.0, count: data[0].count), count: k)
            var clusterCounts = Array(repeating: 0, count: k)
            
            // 分配数据点到最近的质心
            for (i, point) in data.enumerated() {
                var minDistance = Double.infinity
                var closestCentroidIndex = 0
                for (j, centroid) in centroids.enumerated() {
                    let distance = euclideanDistance(point, centroid)
                    if distance < minDistance && distance < 0.3 {
                        minDistance = distance
                        closestCentroidIndex = j
                    }
                }
                labels[i] = closestCentroidIndex
                newCentroids[closestCentroidIndex] = newCentroids[closestCentroidIndex].enumerated().map { index, value in
                    value + point[index]
                }
                clusterCounts[closestCentroidIndex] += 1
            }
            
            // 更新质心
            var hasChanged = false
            for i in 0..<k {
                if clusterCounts[i] > 0 {
                    let newCentroid = newCentroids[i].enumerated().map { index, value in
                        value / Double(clusterCounts[i])
                    }
                    if newCentroid != centroids[i] {
                        hasChanged = true
                        centroids[i] = newCentroid
                    }
                }
            }
            
            // 如果质心没有变化，提前结束迭代
            if !hasChanged {
                break
            }
        }
        
        return labels
    }

    // 计算欧氏距离
    private func euclideanDistance(_ v1: [Double], _ v2: [Double]) -> Double {
        guard v1.count == v2.count else { return Double.infinity }
        let sum = zip(v1, v2).map { pow($0 - $1, 2) }.reduce(0, +)
        return sqrt(sum)
    }

    // 计算向量均值
    private func calculateMean(_ vectors: [[Double]]) -> [Double] {
        guard !vectors.isEmpty else { return [] }
        let count = Double(vectors.count)
        var mean = Array(repeating: 0.0, count: vectors[0].count)
        
        for vector in vectors {
            for (i, value) in vector.enumerated() {
                mean[i] += value / count
            }
        }
        
        return mean
    }
    
    // MARK: - 获取视频第一帧
    private func getFirstFrameHash(for asset: PHAsset) async -> String? {
        // 先检查缓存
        if let cachedHash = await stateManager.getCachedHash(for: asset.localIdentifier) {
            return cachedHash
        }
        
        // 获取第一帧图像
        let image = await getFirstFrame(for: asset)
        guard let image = image else { return nil }
        
        // 计算hash
        let hash = calculateImageHash(image)
        await stateManager.setCachedHash(hash, for: asset.localIdentifier)
        await stateManager.setCachedImage(image, for: asset.localIdentifier)
        
        return hash
    }
    

    // MARK: - 辅助方法
    private func groupVideosByTimeWindow(_ assets: [PHAsset]) -> [[PHAsset]] {
        // 按时间降序排序（新的在前）
        let sortedAssets = assets.sorted { ($0.creationDate ?? Date()) > ($1.creationDate ?? Date()) }
        var timeGroups: [[PHAsset]] = []
        var currentGroup: [PHAsset] = []
        
        if let firstAsset = sortedAssets.first {
            var groupStartTime = firstAsset.creationDate ?? Date()
            
            for asset in sortedAssets {
                let currentTime = asset.creationDate ?? Date()
                // 计算时间差（因为是降序，所以要用 groupStartTime 减 currentTime）
                let timeDiff = groupStartTime.timeIntervalSince(currentTime)
                
                // 如果时间差超过窗口大小，创建新组
                if timeDiff > timeWindowInSeconds {
                    if currentGroup.count > 1 {
                        timeGroups.append(currentGroup)
                    }
                    // 创建新组，并使用当前资源时间作为新的起始时间
                    currentGroup = []
                    groupStartTime = currentTime
                }
                
                currentGroup.append(asset)
            }
            
            // 处理最后一组
            if currentGroup.count > 1 {
                timeGroups.append(currentGroup)
            }
        }
        return timeGroups
    }
    
    private func calculateImageHash(_ image: UIImage) -> String {
        
        guard let cgImage = image.cgImage else { return "" }
        let ciImage = CIImage(cgImage: cgImage)
        
        let size = CGSize(width: 32, height: 32)
          UIGraphicsBeginImageContextWithOptions(size, true, 1.0)
          image.draw(in: CGRect(origin: .zero, size: size))
        guard UIGraphicsGetImageFromCurrentImageContext() != nil else {
              UIGraphicsEndImageContext()
              return ""
          }
          UIGraphicsEndImageContext()
        
        
        guard let filter = CIFilter(name: "CIPhotoEffectNoir"),
              let outputImage = filter.outputImage else {
            return ""
        }
        
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        
        let context = CIContext()
        guard let scaledImage = context.createCGImage(outputImage, from: outputImage.extent),
              let pixelData = UIImage(cgImage: scaledImage).cgImage?.dataProvider?.data,
              let data = CFDataGetBytePtr(pixelData) else {
            return ""
        }
        
        var pixels = Array(repeating: UInt8(0), count: 1024)
        for i in 0..<32 {
            for j in 0..<32 {
                let pixelIndex = (i * 32 + j) * 4
                let gray = UInt8(
                    0.299 * Double(data[pixelIndex]) +
                    0.587 * Double(data[pixelIndex + 1]) +
                    0.114 * Double(data[pixelIndex + 2])
                )
                pixels[i * 32 + j] = gray
            }
        }
        
        let average = UInt8(pixels.reduce(0, { UInt32($0) + UInt32($1) }) / UInt32(pixels.count))
        return pixels.map { $0 > average ? "1" : "0" }.joined()
    }
    
    private func calculateHammingDistance(_ hash1: String, _ hash2: String) -> Int {
        guard hash1.count == hash2.count else { return Int.max }
        return zip(hash1, hash2).filter { $0 != $1 }.count
    }
    
//    private func createAssetModels(from assets: [PHAsset]) -> [AssetModel] {
//        return assets.map { asset in
//            let assetSize: Double
//            if let resource = PHAssetResource.assetResources(for: asset).first,
//               let size = resource.value(forKey: "fileSize") as? Int64 {
//                assetSize = Double(size)
//            } else {
//                assetSize = 0
//            }
//            
//            return AssetModel(
//                localIdentifier: asset.localIdentifier,
//                assetSize: assetSize,
//                createDate: asset.creationDate ?? Date(),
//                mediaType: 2
//            )
//        }
//    }
    
    private func createAssetModels(from assets: [PHAsset]) async -> [AssetModel] {
        return await withTaskGroup(of: AssetModel.self) { modelGroup in
            var models: [AssetModel] = []
            
            for asset in assets {
                modelGroup.addTask {
                    return await withCheckedContinuation { continuation in
                        let assetSize: Double
                        if let resource = PHAssetResource.assetResources(for: asset).first,
                           let size = resource.value(forKey: "fileSize") as? Int64 {
                            assetSize = Double(size)
                        } else {
                            assetSize = 0
                        }
                        
                        let model = AssetModel(
                            localIdentifier: asset.localIdentifier,
                            assetSize: assetSize,
                            createDate: asset.creationDate ?? Date(),
                            mediaType: 2
                        )
                        continuation.resume(returning: model)
                    }
                }
            }
            
            for await model in modelGroup {
                models.append(model)
            }
            return models
        }
    }
    
}

// MARK: - 存储相关方法
extension VideoSimilarJSONManager {
    
    private func loadStoredData() async {
        var loadedTimeGroups: [TimeGroupModel] = []
        var loadedSimilarGroups: [SimilarGroupModel] = []
        
        if let data = try? Data(contentsOf: URL(fileURLWithPath: timeGroupsPath)),
           let groups = try? JSONDecoder().decode([TimeGroupModel].self, from: data) {
            loadedTimeGroups = groups
        }
        
        if let data = try? Data(contentsOf: URL(fileURLWithPath: similarGroupsPath)),
           let groups = try? JSONDecoder().decode([SimilarGroupModel].self, from: data) {
            loadedSimilarGroups = groups
        }
        
        await stateManager.loadStoredData(timeGroups: loadedTimeGroups, similarGroups: loadedSimilarGroups)
    }
    
    private func saveTimeGroup(_ group: TimeGroupModel) async {
        await stateManager.appendTimeGroup(group)
        
        if let data = try? JSONEncoder().encode(await stateManager.getAllTimeGroups()) {
            try? data.write(to: URL(fileURLWithPath: timeGroupsPath))
        }
    }
    
    private func savePendingSimilarGroups() async {
        await stateManager.savePendingGroups()
        
        if let data = try? JSONEncoder().encode(await loadSimilarGroups()) {
            try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
        }
    }
    
    // 移除本地文件资源
    func removeLocalFileWithIds(for ids:[String]) async{
        await stateManager.deleteData(for: ids)
        
        // 保存到文件
        if let data = try? JSONEncoder().encode(await loadSimilarGroups()) {
            try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
        }
    }
    
    private func loadSimilarGroups() async -> [SimilarGroupModel] {
        let groups = await stateManager.getAllSimilarGroups()
        // 验证资源有效性
        return groups.map { group in
            var validAssets = group.assets
            validAssets.removeAll { asset in
                let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [asset.localIdentifier], options: nil)
                return fetchResult.firstObject == nil
            }
            return SimilarGroupModel(groupId: group.groupId, assets: validAssets)
        }.filter { $0.assets.count > 1}
    }
}




//    private func getFirstFrame(for asset: PHAsset) async -> UIImage? {
//        return await withCheckedContinuation { continuation in
//            let options = PHVideoRequestOptions()
//            options.version = .original
//            options.deliveryMode = .fastFormat
//            options.isNetworkAccessAllowed = false
//
//            // 将 options 的配置复制到闭包内
//            PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) {  avAsset, _, _ in
//                guard let videoAsset = avAsset else {
//                    continuation.resume(returning: nil)
//                    return
//                }
//
//                let generator = AVAssetImageGenerator(asset: videoAsset)
//                generator.appliesPreferredTrackTransform = true
//                generator.maximumSize = CGSize(width: 640, height: 640)
//
//                do {
//                    let cgImage = try generator.copyCGImage(at: .zero, actualTime: nil)
//                    let image = UIImage(cgImage: cgImage)
//                    continuation.resume(returning: image)
//                } catch {
//                    continuation.resume(returning: nil)
//                }
//            }
//        }
//    }
 
//    private func getVideoFrameRate(_ asset: PHAsset) async -> Double {
//        return await withCheckedContinuation { continuation in
//            let options = PHVideoRequestOptions()
//            options.version = .original
//            options.deliveryMode = .fastFormat
//            options.isNetworkAccessAllowed = false
//
//            PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) {  avAsset, _, _ in
//                if let videoAsset = avAsset, let track = videoAsset.tracks(withMediaType: .video).first {
//                    continuation.resume(returning: Double(track.nominalFrameRate))
//                } else {
//                    continuation.resume(returning: 0.0)
//                }
//            }
//        }
//    }
