@preconcurrency import AVFoundation
import Photos


class VideoCompressor {
    private static let compressionQueue = DispatchQueue(label: "com.compression.queue", qos: .userInitiated)

    static func compressVideos(
        models: [AssetModel],
        quality: Float,
        progress: @escaping (Float) -> Void,
        completion: @escaping ([URL?], [Error?]) -> Void
    ) {
        var results = [URL?](repeating: nil, count: models.count)
        var errors = [Error?](repeating: nil, count: models.count)
        let group = DispatchGroup()
        var totalProgress: Float = 0
        let totalVideos = Float(models.count)
        var individualProgress = [Float](repeating: 0, count: models.count)

        for (index, model) in models.enumerated() {
            group.enter()

            fetchAVAsset(for: model) { asset, error in
                guard let asset = asset else {
                    errors[index] = error ?? NSError(domain: "AssetError", code: -1, userInfo: nil)
                    group.leave()
                    return
                }
                // 这里再添加一个逻辑 当原大小小于等于为100时压缩比例调成0.98
                var tempScale = quality
                if model.assetSize <= 102400 {
                    tempScale = 0.98
                }

                compressionQueue.async {
                    compressSingleVideo(
                        asset: asset,
                        quality: tempScale,
                        progress: { p in
                            DispatchQueue.main.async {
                                // 更新单个视频的进度
                                individualProgress[index] = p
                                // 重新计算总进度
                                totalProgress = individualProgress.reduce(0, +) / totalVideos
                                progress(totalProgress)
                            }
                        },
                        completion: { url, error in
                            results[index] = url
                            errors[index] = error
                            group.leave()
                        }
                    )
                }
            }
        }

        group.notify(queue: .main) {
            completion(results, errors)
        }
    }

    // MARK: - 核心压缩方法（闭包版本）
    private static func compressSingleVideo(
        asset: AVAsset,
        quality: Float,
        progress: @escaping (Float) -> Void,
        completion: @escaping (URL?, Error?) -> Void
    ) {
        let outputURL = URL(fileURLWithPath: NSTemporaryDirectory())
           .appendingPathComponent(UUID().uuidString)
           .appendingPathExtension("mp4")

        // 清理已存在的文件
        try? FileManager.default.removeItem(at: outputURL)

        // 异步加载视频轨道
        loadTracksAsync(asset: asset) { videoTrack, audioTrack, error in
            guard let videoTrack = videoTrack else {
                completion(nil, error ?? CompressionError.invalidVideoTrack)
                return
            }

            do {
                let (reader, videoOutput, writer, videoInput) = try setupVideoComponents(
                    videoTrack: videoTrack,
                    outputURL: outputURL,
                    quality: quality
                )

                let (audioOutput, audioInput) = try setupAudioComponents(audioTrack: audioTrack)

                // 配置读写器
                if let audioOutput = audioOutput, let audioInput = audioInput {
                    reader.add(audioOutput)
                    writer.add(audioInput)
                }

                // 开始处理
                processSamples(
                    reader: reader,
                    writer: writer,
                    videoOutput: videoOutput,
                    videoInput: videoInput,
                    audioOutput: audioOutput,
                    audioInput: audioInput,
                    duration: CMTimeGetSeconds(asset.duration),
                    progress: progress,
                    completion: { result in
                        switch result {
                        case .success:
                            completion(outputURL, nil)
                        case .failure(let error):
                            completion(nil, error)
                        }
                    }
                )
            } catch {
                completion(nil, error)
            }
        }
    }

    // MARK: - 异步加载轨道（兼容iOS 14）
    private static func loadTracksAsync(
        asset: AVAsset,
        completion: @escaping (AVAssetTrack?, AVAssetTrack?, Error?) -> Void
    ) {
        let keys: [String] = [
            #keyPath(AVAsset.tracks),
            #keyPath(AVAsset.duration)
        ]

        asset.loadValuesAsynchronously(forKeys: keys) {
            var error: NSError?
            let status = asset.statusOfValue(forKey: #keyPath(AVAsset.tracks), error: &error)

            guard status == .loaded else {
                completion(nil, nil, error ?? CompressionError.trackLoadingFailed)
                return
            }

            let videoTrack = asset.tracks(withMediaType: .video).first
            let audioTrack = asset.tracks(withMediaType: .audio).first
            completion(videoTrack, audioTrack, nil)
        }
    }

    // MARK: - 视频组件配置
    private static func setupVideoComponents(
        videoTrack: AVAssetTrack,
        outputURL: URL,
        quality: Float
    ) throws -> (AVAssetReader, AVAssetReaderTrackOutput, AVAssetWriter, AVAssetWriterInput) {
        // 视频读取配置
        let readerOutputSettings: [String: Any] = [
            kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
        ]

        guard let reader = try? AVAssetReader(asset: videoTrack.asset!) else {
            throw CompressionError.readerInitializationFailed
        }

        let videoOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: readerOutputSettings)
        guard reader.canAdd(videoOutput) else {
            throw CompressionError.readerInitializationFailed
        }
        reader.add(videoOutput)

        // 视频写入配置
        guard let writer = try? AVAssetWriter(outputURL: outputURL, fileType: .mp4) else {
            throw CompressionError.writerInitializationFailed
        }

        let originalSize = videoTrack.naturalSize
        let originalBitrate = Double(videoTrack.estimatedDataRate)
        let targetSize = calculateTargetSize(originalSize: originalSize, quality: quality)

        let videoInput = AVAssetWriterInput(
            mediaType: .video,
            outputSettings: [
                AVVideoCodecKey: AVVideoCodecType.h264,
                AVVideoWidthKey: targetSize.width,
                AVVideoHeightKey: targetSize.height,
                AVVideoCompressionPropertiesKey: [
                    AVVideoAverageBitRateKey: quality * Float(originalBitrate),
                    AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
                    AVVideoMaxKeyFrameIntervalKey: 30
                ]
            ]
        )
        videoInput.transform = videoTrack.preferredTransform

        guard writer.canAdd(videoInput) else {
            throw CompressionError.writerInitializationFailed
        }
        writer.add(videoInput)

        return (reader, videoOutput, writer, videoInput)
    }

    // MARK: - 音频组件配置
    private static func setupAudioComponents(
        audioTrack: AVAssetTrack?
    ) throws -> (AVAssetReaderTrackOutput?, AVAssetWriterInput?) {
        guard let audioTrack = audioTrack else {
            return (nil, nil)
        }

        // 音频读取配置
        let audioOutput = AVAssetReaderTrackOutput(
            track: audioTrack,
            outputSettings: [
                AVFormatIDKey: kAudioFormatLinearPCM
            ]
        )

        // 音频写入配置
        let audioInput = AVAssetWriterInput(
            mediaType: .audio,
            outputSettings: [
                AVFormatIDKey: kAudioFormatMPEG4AAC,
                AVSampleRateKey: 44100,
                AVEncoderBitRateKey: 128000,
                AVNumberOfChannelsKey: 2
            ]
        )

        return (audioOutput, audioInput)
    }

    // MARK: - 处理样本数据
    // 修改后的 processSamples 方法
    private static func processSamples(
        reader: AVAssetReader,
        writer: AVAssetWriter,
        videoOutput: AVAssetReaderTrackOutput,
        videoInput: AVAssetWriterInput,
        audioOutput: AVAssetReaderTrackOutput?,
        audioInput: AVAssetWriterInput?,
        duration: Float64,
        progress: @escaping (Float) -> Void,
        completion: @escaping (Result<Void, Error>) -> Void
    ) {
        let videoQueue = DispatchQueue(label: "video.queue")
        let audioQueue = DispatchQueue(label: "audio.queue")
        let stateQueue = DispatchQueue(label: "state.queue")

        // 使用线程安全的状态管理
        var _videoFinished = false
        var _audioFinished = false

        func updateVideoFinished(_ value: Bool) {
            stateQueue.async {
                _videoFinished = value
                checkCompletion()
            }
        }

        func updateAudioFinished(_ value: Bool) {
            stateQueue.async {
                _audioFinished = value
                checkCompletion()
            }
        }

        func checkCompletion() {
            stateQueue.async {
                guard _videoFinished && _audioFinished else { return }

                writer.finishWriting {
                    // 切换到主队列处理完成回调
                    DispatchQueue.main.async {
                        switch writer.status {
                        case .completed:
                            completion(.success(()))
                        case .failed, .cancelled:
                            completion(.failure(writer.error ?? CompressionError.exportFailed))
                        default:
                            completion(.failure(CompressionError.exportFailed))
                        }
                    }
                }
            }
        }

        writer.startWriting()
        reader.startReading()
        writer.startSession(atSourceTime: .zero)

        // 处理视频样本
        videoInput.requestMediaDataWhenReady(on: videoQueue) {
            while videoInput.isReadyForMoreMediaData {
                guard reader.status == .reading,
                      let sampleBuffer = videoOutput.copyNextSampleBuffer() else {
                    videoInput.markAsFinished()
                    updateVideoFinished(true)
                    break
                }

                let time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                let currentProgress = Float(time.seconds / duration)

                DispatchQueue.main.async {
                    progress(currentProgress)
                }

                videoInput.append(sampleBuffer)
            }
        }

        // 处理音频样本
        if let audioInput = audioInput, let audioOutput = audioOutput {
            audioInput.requestMediaDataWhenReady(on: audioQueue) {
                while audioInput.isReadyForMoreMediaData {
                    guard let sampleBuffer = audioOutput.copyNextSampleBuffer() else {
                        audioInput.markAsFinished()
                        updateAudioFinished(true)
                        break
                    }
                    audioInput.append(sampleBuffer)
                }
            }
        } else {
            updateAudioFinished(true)
        }
    }

    // MARK: - 其他工具方法
    private static func calculateTargetSize(originalSize: CGSize, quality: Float) -> CGSize {
        let scale = max(0.1, min(1.0, quality))
        return CGSize(
            width: round(originalSize.width * CGFloat(scale)),
            height: round(originalSize.height * CGFloat(scale))
        )
    }

    private static func fetchAVAsset(
        for model: AssetModel,
        completion: @escaping (AVAsset?, Error?) -> Void
    ) {
        let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [model.localIdentifier], options: nil)
        guard let phAsset = fetchResult.firstObject else {
            completion(nil, CompressionError.assetNotFound)
            return
        }

        let options = PHVideoRequestOptions()
        options.isNetworkAccessAllowed = true

        PHImageManager.default().requestAVAsset(
            forVideo: phAsset,
            options: options
        ) { asset, _, info in
            completion(asset, info?[PHImageErrorKey] as? Error)
        }
    }

    enum CompressionError: Error {
        case readerInitializationFailed
        case writerInitializationFailed
        case invalidVideoTrack
        case trackLoadingFailed
        case exportFailed
        case assetNotFound
    }
}
