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

import Foundation
import Photos
import UIKit


class ScreenshotSimilarJSONManager: @unchecked Sendable {
    
    static let shared = ScreenshotSimilarJSONManager()
    private let stateManager = PhotoSimilarStateManager()
    private init() {}
    
    // MARK: - 配置参数
    private let timeWindowInSeconds: TimeInterval = 600 // 10分钟时间窗口
    private let fileSizeThreshold: Double = 0.01 // 文件大小相差阈值（1%）
    private let resolutionThreshold: Double = 0.01 // 分辨率相差阈值（1%）

    
    // 文件路径
    private var timeGroupsPath: String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0].appendingPathComponent("screenshotTimeGroups.json").path
    }
    
    private var similarGroupsPath: String {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0].appendingPathComponent("screenshotSimilarGroups.json").path
    }
    
    private var currentTask: Task<Void, Error>?
    
    // 最新照片时间戳存储key
    private let latestPhotoTimeKey = "screenshotLatestPhotoTimestamp"
    

    func findSimilarAssets(in assets: [PHAsset],
                          mediaType: MediaType = .photo,
                          progressHandler: (([AssetModel]) -> Void)?,
                          completionHandler: (([[AssetModel]]) -> Void)?) {
        
        Task {
            // 1. 加载本地数据
            await loadStoredData()
            print("本地数据加载完成")
            
            // 2. 获取上次记录的最新资源时间戳
            var lastLatestTime = UserDefaults.standard.double(forKey: latestPhotoTimeKey)
            
            if lastLatestTime == 0{
                // 如果没拿到 说吗是第一次执行，直接拿第一个记录最新资源时间
                lastLatestTime = assets.first?.creationDate?.timeIntervalSince1970 ?? 0
            }
            
            // 3. 通知已缓存的结果
            let cachedGroups = await loadSimilarGroups() //await stateManager.getAllSimilarGroups()
            print("通知已缓存的结果", cachedGroups.count)
            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: latestPhotoTimeKey)
                UserDefaults.standard.synchronize()
            }
            
            let newtimeGroup = groupAssetsByTimeWindow(newAssets)
            let oldGroups = groupAssetsByTimeWindow(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)
                    }
                }
            }
            
            print("开始处理分组，分组资源为：",unprocessedGroups.count)
            
            let maxConcurrency = 2 // 最大并发数
            let batchSize = max(1, unprocessedGroups.count / maxConcurrency)
            
            if unprocessedGroups.count == 0{
                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
                    for unGroup in batch {
                        group.addTask { [weak self] in
                            // 原任务代码
                            guard let self = self else {
                                print("self 为 nil，任务提前退出")
                                return
                            }
                            // 6.1 按文件大小预分组
                            let sizeGroups = self.groupAssetsBySize(unGroup)
                            // 6.2 处理每个大小组
                            for sizeGroup in sizeGroups {
                                let similarGroups = await self.findSimilarInGroupUsingKMeans(sizeGroup)
                                if !similarGroups.isEmpty {
                                    for similarGroup in similarGroups {
                                        let groupId = UUID().uuidString
                                        // 6.3 创建资源模型
                                        let assetModels = await createAssetModels(from: similarGroup)
                                        
                                        // 6.4 通知进度
                                        await MainActor.run {
                                            progressHandler?(assetModels)
                                        }
                                        
                                        // 6.5 保存相似组
                                        await stateManager.appendSimilarGroup(SimilarGroupModel(groupId: groupId, assets: assetModels))
                                        
                                        if await stateManager.shouldSavePendingGroups() {
                                            await 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 {
                print("执行完毕")
                completionHandler?(allGroups.map { $0.assets })
            }
        }
    }
    
    // 创建资源模型
//    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 options = PHImageRequestOptions()
//                        options.isSynchronous = false
//                        options.version = .original
//                        options.isNetworkAccessAllowed = false
//                        PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
//                            let assetSize = Double(data?.count ?? 0)
//                            let model = AssetModel(
//                                localIdentifier: asset.localIdentifier,
//                                assetSize: assetSize,
//                                createDate: asset.creationDate ?? Date(),
//                                mediaType: 1
//                            )
//                            continuation.resume(returning: model)
//                        }
//                    }
//                }
//            }
//            
//            for await model in modelGroup {
//                models.append(model)
//            }
//            return models
//        }
//    }
    
    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: 1
                        )
                        continuation.resume(returning: model)
                    }
                }
            }
            
            for await model in modelGroup {
                models.append(model)
            }
            return models
        }
    }
    
}

// MARK: - 分组辅助方法
extension ScreenshotSimilarJSONManager{
    
    nonisolated private func groupAssetsByTimeWindow(_ 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
    }
    
    
    nonisolated  private func groupAssetsBySize(_ assets: [PHAsset]) -> [[PHAsset]] {
        var sizeGroups: [[PHAsset]] = []
        var processedAssets = Set<String>()
        
        for asset in assets {
            if processedAssets.contains(asset.localIdentifier) {
                continue
            }
            
            var currentGroup = [asset]
            processedAssets.insert(asset.localIdentifier)
            
            // 查找大小相近的资产
            for compareAsset in assets {
                if processedAssets.contains(compareAsset.localIdentifier) {
                    continue
                }
                
                if isFileSizeSimilar(asset, compareAsset) {
                    currentGroup.append(compareAsset)
                    processedAssets.insert(compareAsset.localIdentifier)
                }
            }
            
            if currentGroup.count > 1 {
                sizeGroups.append(currentGroup)
            }
        }
        
        return sizeGroups
    }
    // 文件大小比较
    nonisolated  private func isFileSizeSimilar(_ asset1: PHAsset, _ asset2: PHAsset) -> Bool {
        let size1 = Double(asset1.pixelWidth * asset1.pixelHeight)
        let size2 = Double(asset2.pixelWidth * asset2.pixelHeight)
        let ratio = abs(size1 - size2) / max(size1, size2)
        return ratio <= fileSizeThreshold
    }
}

// MARK: - 存储相关方法
extension ScreenshotSimilarJSONManager{
    
    //加载本地存储资源
    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))
        }
    }
    
    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}
    }
    
    
    // 移除本地文件资源
    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))
        }
    }
}

// MARK: - pHash获取
extension ScreenshotSimilarJSONManager{
    
    // 计算图片hash
    private func getOrCalculateHash(for asset: PHAsset) async -> String? {
        if let cachedHash = await stateManager.getCachedHash(for: asset.localIdentifier) {
            print("返回缓存cachedHash")
            return cachedHash
        }
        
        if let cachedImage = await stateManager.getCachedImage(for: asset.localIdentifier) {
            let hash = calculateImageHash(cachedImage)
            print("返回缓存hash")
            await stateManager.setCachedHash(hash, for: asset.localIdentifier)
            return hash
        }
        
        let options = PHImageRequestOptions()
        options.version = .original
        let targetSize = CGSize(width: 32, height: 32)
        return await withCheckedContinuation { continuation in
            PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options) { [weak self] image, _ in
                if let image = image, let self = self {
                    let tempHash = self.calculateImageHash(image)
                    
                    Task {
                        await self.stateManager.setCachedImage(image, for: asset.localIdentifier)
                        await self.stateManager.setCachedHash(tempHash, for: asset.localIdentifier)
                    }
                    
                    continuation.resume(returning: tempHash)
                } else {
                    continuation.resume(returning: nil)
                }
            }
        }
    }
    
    // 计算图片hash
    private func calculateImageHash(_ image: UIImage) -> String {
        guard let cgImage = image.cgImage else { return "" }
        let ciImage = CIImage(cgImage: cgImage)
        
        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
    }
    
    func cancelCurrentOperation() {
         currentTask?.cancel()
     }
}

// MARK: - 相似度聚类算法
extension ScreenshotSimilarJSONManager{
    
    // 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
    }

    // 计算欧几里得距离
    func euclideanDistance(_ point1: [Double], _ point2: [Double]) -> Double {
        let squaredSum = zip(point1, point2).map { pow($0 - $1, 2) }.reduce(0, +)
        return sqrt(squaredSum)
    }

    // 将哈希值转换为数值向量
    func hashToVector(_ hash: String) -> [Double] {
        return hash.map { $0 == "1" ? 1.0 : 0.0 }
    }

    private func findSimilarInGroupUsingKMeans(_ assets: [PHAsset]) async -> [[PHAsset]] {
        // 获取所有资产的哈希值
        let hashes = await withTaskGroup(of: String?.self) { group in
            for asset in assets {
                group.addTask {
                    return await self.getOrCalculateHash(for: asset)
                }
            }
            var result: [String?] = []
            for await hash in group {
                result.append(hash)
            }
            return result
        }
        
        // 将哈希值转换为数值向量
        let vectors = hashes.compactMap { $0.map { hashToVector($0) } }
        
        // 使用 K-Means 聚类算法
        let k = min(assets.count, 10) // 假设最多 10 个簇
        let labels = kMeansClustering(data: vectors, k: k)
        
        // 根据聚类结果分组
        var clusters: [[PHAsset]] = Array(repeating: [], count: k)
        for (i, label) in labels.enumerated() {
            clusters[label].append(assets[i])
        }
        
        // 过滤掉只有一个元素的簇
        return clusters.filter { $0.count > 1 }
    }
}


