import Accelerate
import AVFoundation
import Foundation

/// Generates waveform data from audio files for visualization.
struct WaveformGenerator {

    struct WaveformSample: Codable {
        let min: Float
        let max: Float
    }

    /// Generate downsampled waveform from an audio file.
    static func generateWaveform(for track: Track, targetSampleCount: Int = 300) async throws -> [WaveformSample] {
        let url = track.fileURL
        let file = try AVAudioFile(forReading: url)

        guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: AVAudioFrameCount(file.length)) else {
            return []
        }
        try file.read(into: buffer)

        guard let floatData = buffer.floatChannelData else { return [] }
        let frameCount = Int(buffer.frameLength)
        let channelCount = Int(buffer.format.channelCount)

        // Mix to mono
        var monoSamples = [Float](repeating: 0, count: frameCount)
        for ch in 0..<channelCount {
            let channelPtr = floatData[ch]
            for i in 0..<frameCount {
                monoSamples[i] += channelPtr[i]
            }
        }
        if channelCount > 1 {
            var divisor = Float(channelCount)
            vDSP_vsdiv(monoSamples, 1, &divisor, &monoSamples, 1, vDSP_Length(frameCount))
        }

        // Downsample to target count
        let samplesPerBucket = max(1, frameCount / targetSampleCount)
        var waveform: [WaveformSample] = []
        waveform.reserveCapacity(targetSampleCount)

        for i in 0..<targetSampleCount {
            let start = i * samplesPerBucket
            let end = min(start + samplesPerBucket, frameCount)
            let count = vDSP_Length(end - start)
            guard count > 0 else { continue }

            var minVal: Float = 0
            var maxVal: Float = 0
            monoSamples.withUnsafeBufferPointer { ptr in
                let base = ptr.baseAddress! + start
                vDSP_minv(base, 1, &minVal, count)
                vDSP_maxv(base, 1, &maxVal, count)
            }
            waveform.append(WaveformSample(min: minVal, max: maxVal))
        }

        // Cache on the track
        if let encoded = try? JSONEncoder().encode(waveform) {
            track.waveformData = encoded
        }

        return waveform
    }

    /// Decode cached waveform data.
    static func decodeCachedWaveform(from data: Data) -> [WaveformSample]? {
        try? JSONDecoder().decode([WaveformSample].self, from: data)
    }
}
