import AudioToolbox
import AVFoundation
import Foundation
import os

// MARK: - WebM/Opus → M4A Converter

/// Converts WebM/Opus audio (from Desktop) to M4A/AAC playable by AVAudioPlayer.
/// iOS doesn't support WebM container natively. This parser extracts Opus frames from
/// the EBML/WebM container, wraps them in a CAF file (Apple's native container that
/// supports Opus), then transcodes to M4A via AVAudioFile.
enum WebMOpusConverter {

    private static let logger = Logger(subsystem: "com.rosetta.messenger", category: "WebMOpusConverter")

    /// WebM/EBML magic: `0x1A 0x45 0xDF 0xA3`
    static func isWebM(_ data: Data) -> Bool {
        data.count >= 4 && data[0] == 0x1A && data[1] == 0x45 && data[2] == 0xDF && data[3] == 0xA3
    }

    /// Main entry: WebM/Opus Data → M4A Data (or CAF fallback).
    /// Returns nil only if WebM parsing fails entirely.
    static func convertToPlayable(_ webmData: Data) -> Data? {
        guard let parsed = parseWebM(webmData) else {
            logger.error("WebM parse failed (\(webmData.count) bytes)")
            return nil
        }
        guard !parsed.frames.isEmpty else {
            logger.warning("WebM contains 0 Opus frames")
            return nil
        }
        logger.info("WebM parsed: \(parsed.frames.count) frames, \(parsed.sampleRate)Hz, \(parsed.channels)ch")

        // Decode Opus → PCM via AudioConverter, then write as WAV
        if let wavData = decodeOpusToWAV(parsed) {
            logger.info("WebM→WAV conversion succeeded (\(wavData.count) bytes)")
            return wavData
        }

        // Fallback: try CAF container route
        if let cafData = buildCAF(parsed), let m4aData = transcodeCAFtoM4A(cafData) {
            return m4aData
        }

        logger.error("All conversion paths failed")
        return nil
    }

    // MARK: - WebM/EBML Parser

    private struct ParsedWebM {
        let frames: [Data]
        let sampleRate: Double
        let channels: UInt32
        let codecPrivate: Data?
    }

    private static func parseWebM(_ data: Data) -> ParsedWebM? {
        let bytes = [UInt8](data)
        var pos = 0

        // EBML header (0x1A45DFA3)
        guard bytes.count >= 8,
              bytes[0] == 0x1A, bytes[1] == 0x45, bytes[2] == 0xDF, bytes[3] == 0xA3
        else { return nil }
        pos = 4
        guard let (headerSize, hLen) = readVINTValue(bytes, at: pos) else { return nil }
        pos += hLen + Int(headerSize)

        // Segment (0x18538067)
        guard pos + 4 <= bytes.count,
              bytes[pos] == 0x18, bytes[pos + 1] == 0x53,
              bytes[pos + 2] == 0x80, bytes[pos + 3] == 0x67
        else { return nil }
        pos += 4
        guard let (_, segLen) = readVINTValue(bytes, at: pos) else { return nil }
        pos += segLen

        var sampleRate: Double = 48000
        var channels: UInt32 = 1
        var codecPrivate: Data?
        var audioTrackNum: UInt64 = 1
        var frames: [Data] = []
        frames.reserveCapacity(512)

        while pos < bytes.count {
            guard let (elemID, idLen) = readElementID(bytes, at: pos) else { break }
            pos += idLen
            guard let (elemSize, sizeLen) = readVINTValue(bytes, at: pos) else { break }
            pos += sizeLen

            let elemDataEnd = min(pos + Int(elemSize), bytes.count)

            switch elemID {
            case 0x1654AE6B: // Tracks — parse children (TrackEntry)
                parseTracks(bytes, from: pos, to: elemDataEnd,
                            sampleRate: &sampleRate, channels: &channels,
                            codecPrivate: &codecPrivate, audioTrackNum: &audioTrackNum)
                pos = elemDataEnd

            case 0x1F43B675: // Cluster — extract SimpleBlocks
                parseCluster(bytes, from: pos, to: elemDataEnd,
                             audioTrackNum: audioTrackNum, frames: &frames)
                pos = elemDataEnd

            default:
                pos = elemDataEnd
            }

            if pos <= 0 || elemDataEnd <= 0 { break }
        }

        return ParsedWebM(frames: frames, sampleRate: sampleRate,
                          channels: channels, codecPrivate: codecPrivate)
    }

    private static func parseTracks(
        _ bytes: [UInt8], from start: Int, to end: Int,
        sampleRate: inout Double, channels: inout UInt32,
        codecPrivate: inout Data?, audioTrackNum: inout UInt64
    ) {
        var pos = start
        while pos < end {
            guard let (id, idLen) = readElementID(bytes, at: pos) else { break }
            pos += idLen
            guard let (size, sizeLen) = readVINTValue(bytes, at: pos) else { break }
            pos += sizeLen
            let childEnd = min(pos + Int(size), end)

            if id == 0xAE { // TrackEntry
                parseTrackEntry(bytes, from: pos, to: childEnd,
                                sampleRate: &sampleRate, channels: &channels,
                                codecPrivate: &codecPrivate, audioTrackNum: &audioTrackNum)
            }
            pos = childEnd
        }
    }

    private static func parseTrackEntry(
        _ bytes: [UInt8], from start: Int, to end: Int,
        sampleRate: inout Double, channels: inout UInt32,
        codecPrivate: inout Data?, audioTrackNum: inout UInt64
    ) {
        var pos = start
        var isAudio = false
        var trackNum: UInt64 = 0

        while pos < end {
            guard let (id, idLen) = readElementID(bytes, at: pos) else { break }
            pos += idLen
            guard let (size, sizeLen) = readVINTValue(bytes, at: pos) else { break }
            pos += sizeLen
            let childEnd = min(pos + Int(size), end)

            switch id {
            case 0xD7: // TrackNumber
                trackNum = readUInt(bytes, at: pos, count: Int(size))
            case 0x83: // TrackType (2 = audio)
                isAudio = readUInt(bytes, at: pos, count: Int(size)) == 2
            case 0x63A2: // CodecPrivate (OpusHead)
                if childEnd <= bytes.count {
                    codecPrivate = Data(bytes[pos..<childEnd])
                }
            case 0xE1: // Audio sub-element
                parseAudioElement(bytes, from: pos, to: childEnd,
                                  sampleRate: &sampleRate, channels: &channels)
            default:
                break
            }
            pos = childEnd
        }

        if isAudio { audioTrackNum = trackNum }
    }

    private static func parseAudioElement(
        _ bytes: [UInt8], from start: Int, to end: Int,
        sampleRate: inout Double, channels: inout UInt32
    ) {
        var pos = start
        while pos < end {
            guard let (id, idLen) = readElementID(bytes, at: pos) else { break }
            pos += idLen
            guard let (size, sizeLen) = readVINTValue(bytes, at: pos) else { break }
            pos += sizeLen
            let childEnd = min(pos + Int(size), end)

            switch id {
            case 0xB5: // SamplingFrequency (float)
                sampleRate = readFloat(bytes, at: pos, count: Int(size))
            case 0x9F: // Channels
                channels = UInt32(readUInt(bytes, at: pos, count: Int(size)))
            default:
                break
            }
            pos = childEnd
        }
    }

    private static func parseCluster(
        _ bytes: [UInt8], from start: Int, to end: Int,
        audioTrackNum: UInt64, frames: inout [Data]
    ) {
        var pos = start
        while pos < end {
            guard let (id, idLen) = readElementID(bytes, at: pos) else { break }
            pos += idLen
            guard let (size, sizeLen) = readVINTValue(bytes, at: pos) else { break }
            pos += sizeLen
            let childEnd = min(pos + Int(size), end)

            if id == 0xA3 { // SimpleBlock
                guard let (blockTrack, trackLen) = readVINTValue(bytes, at: pos) else {
                    pos = childEnd; continue
                }
                if blockTrack == audioTrackNum {
                    let headerStart = pos + trackLen
                    // 2 bytes timestamp + 1 byte flags
                    guard headerStart + 3 <= childEnd else { pos = childEnd; continue }
                    let flags = bytes[headerStart + 2]
                    let lacingType = (flags >> 1) & 0x03 // bits 1-2 in Matroska spec
                    let dataStart = headerStart + 3

                    if lacingType == 0 {
                        // No lacing — single frame
                        if dataStart < childEnd {
                            frames.append(Data(bytes[dataStart..<childEnd]))
                        }
                    } else {
                        // Laced: first byte = number of frames - 1
                        guard dataStart < childEnd else { pos = childEnd; continue }
                        let numFrames = Int(bytes[dataStart]) + 1
                        let lacedDataStart = dataStart + 1

                        if lacingType == 0x02 { // Fixed-size lacing
                            let totalData = childEnd - lacedDataStart
                            let frameSize = totalData / numFrames
                            for i in 0..<numFrames {
                                let fStart = lacedDataStart + i * frameSize
                                let fEnd = min(fStart + frameSize, childEnd)
                                if fStart < fEnd { frames.append(Data(bytes[fStart..<fEnd])) }
                            }
                        } else if lacingType == 0x01 { // Xiph lacing
                            var frameSizes = [Int]()
                            var sizePos = lacedDataStart
                            for _ in 0..<(numFrames - 1) {
                                var frameSize = 0
                                while sizePos < childEnd {
                                    let val = Int(bytes[sizePos]); sizePos += 1
                                    frameSize += val
                                    if val < 255 { break }
                                }
                                frameSizes.append(frameSize)
                            }
                            // Last frame = remaining data
                            let consumed = frameSizes.reduce(0, +)
                            frameSizes.append(childEnd - sizePos - consumed)

                            var fPos = sizePos
                            for sz in frameSizes {
                                let fEnd = min(fPos + sz, childEnd)
                                if fPos < fEnd { frames.append(Data(bytes[fPos..<fEnd])) }
                                fPos = fEnd
                            }
                        } else if lacingType == 0x03 { // EBML lacing
                            var frameSizes = [Int]()
                            var sizePos = lacedDataStart
                            // First frame size: unsigned VINT
                            if let (firstSize, firstLen) = readVINTValue(bytes, at: sizePos) {
                                frameSizes.append(Int(firstSize))
                                sizePos += firstLen
                            }
                            // Subsequent sizes: signed VINT deltas
                            for _ in 1..<(numFrames - 1) {
                                guard let (raw, len) = readVINTValue(bytes, at: sizePos) else { break }
                                sizePos += len
                                // Convert to signed: subtract midpoint
                                let midpoint = (1 << (len * 7 - 1)) - 1
                                let delta = Int(raw) - midpoint
                                let prev = frameSizes.last ?? 0
                                frameSizes.append(prev + delta)
                            }
                            // Last frame = remaining
                            let consumed = frameSizes.reduce(0, +)
                            frameSizes.append(childEnd - sizePos - consumed)

                            var fPos = sizePos
                            for sz in frameSizes {
                                let fEnd = min(fPos + max(0, sz), childEnd)
                                if fPos < fEnd { frames.append(Data(bytes[fPos..<fEnd])) }
                                fPos = fEnd
                            }
                        }
                    }
                }
            }
            pos = childEnd
        }
    }

    // MARK: - Opus → PCM → WAV (AudioToolbox AudioConverter)

    /// Base frame duration in samples from Opus TOC byte (single frame, no count multiplier).
    /// AudioConverter needs this for mFramesPerPacket (max 5760 = 120ms).
    private static func opusBaseFrameSamples(_ packet: Data, sampleRate: Double) -> UInt32 {
        guard !packet.isEmpty else { return 960 }
        let config = Int((packet[0] >> 3) & 0x1F)
        let durationMs: Int
        switch config {
        case 0...3:   durationMs = 10
        case 4...7:   durationMs = 20
        case 8...11:  durationMs = 40
        case 12...15: durationMs = 60
        case 16...19: durationMs = 10
        case 20...23: durationMs = 20
        case 24...27: durationMs = 10
        case 28...31: durationMs = 20
        default:      durationMs = 20
        }
        return UInt32(Int(sampleRate) * durationMs / 1000)
    }

    /// Total PCM samples this Opus packet will produce (base × frame count).
    private static func opusTotalSamples(_ packet: Data, sampleRate: Double) -> UInt32 {
        let base = opusBaseFrameSamples(packet, sampleRate: sampleRate)
        guard !packet.isEmpty else { return base }
        let code = packet[0] & 0x03
        let count: UInt32
        switch code {
        case 0: count = 1
        case 1, 2: count = 2
        case 3: count = packet.count > 1 ? UInt32(packet[1] & 0x3F) : 1
        default: count = 1
        }
        return base * count
    }

    private static func decodeOpusToWAV(_ parsed: ParsedWebM) -> Data? {
        let sampleRate = parsed.sampleRate
        let channels = parsed.channels

        // Base frame size for ASBD (single sub-frame, max 5760 = 120ms)
        let baseSamples = parsed.frames.first.map {
            opusBaseFrameSamples($0, sampleRate: sampleRate)
        } ?? 960

        logger.info("Opus base frame: \(baseSamples) samples (\(Double(baseSamples) / sampleRate * 1000)ms)")

        // Input: Opus compressed (base frame size for AudioConverter)
        var inASBD = AudioStreamBasicDescription(
            mSampleRate: sampleRate,
            mFormatID: kAudioFormatOpus,
            mFormatFlags: 0,
            mBytesPerPacket: 0,
            mFramesPerPacket: baseSamples,
            mBytesPerFrame: 0,
            mChannelsPerFrame: channels,
            mBitsPerChannel: 0,
            mReserved: 0
        )

        // Output: 16-bit PCM
        let bytesPerSample: UInt32 = 2
        var outASBD = AudioStreamBasicDescription(
            mSampleRate: sampleRate,
            mFormatID: kAudioFormatLinearPCM,
            mFormatFlags: kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
            mBytesPerPacket: bytesPerSample * channels,
            mFramesPerPacket: 1,
            mBytesPerFrame: bytesPerSample * channels,
            mChannelsPerFrame: channels,
            mBitsPerChannel: bytesPerSample * 8,
            mReserved: 0
        )

        var converter: AudioConverterRef?
        var status = AudioConverterNew(&inASBD, &outASBD, &converter)
        guard status == noErr, let converter else {
            logger.warning("AudioConverterNew failed for Opus: \(status)")
            return nil
        }
        defer { AudioConverterDispose(converter) }

        // Set magic cookie (OpusHead) if available
        if var cookie = parsed.codecPrivate.map({ [UInt8]($0) }), !cookie.isEmpty {
            AudioConverterSetProperty(
                converter,
                kAudioConverterDecompressionMagicCookie,
                UInt32(cookie.count),
                &cookie
            )
        }

        // Buffer sized for max multi-frame packet (6 × 60ms = 360ms = 17280 samples)
        let maxOutputSamples: UInt32 = 17280
        let pcmBufferSize = Int(maxOutputSamples * bytesPerSample * channels)
        var allPCM = Data()
        allPCM.reserveCapacity(parsed.frames.count * Int(baseSamples * bytesPerSample * channels))

        for frame in parsed.frames {
            // Per-packet total samples for output request
            let totalSamples = opusTotalSamples(frame, sampleRate: sampleRate)

            var pcmBuffer = [UInt8](repeating: 0, count: pcmBufferSize)
            var outBufferList = AudioBufferList(
                mNumberBuffers: 1,
                mBuffers: AudioBuffer(
                    mNumberChannels: channels,
                    mDataByteSize: UInt32(pcmBufferSize),
                    mData: &pcmBuffer
                )
            )
            var ioOutputDataPacketSize: UInt32 = max(totalSamples, maxOutputSamples)

            // Packet description for this Opus frame
            var packetDesc = AudioStreamPacketDescription(
                mStartOffset: 0,
                mVariableFramesInPacket: 0,
                mDataByteSize: UInt32(frame.count)
            )

            let inputContext = UnsafeMutablePointer<OpusInputContext>.allocate(capacity: 1)
            inputContext.pointee = OpusInputContext(data: frame, packetDesc: packetDesc, consumed: false)
            defer { inputContext.deallocate() }

            status = AudioConverterFillComplexBuffer(
                converter,
                opusInputCallback,
                inputContext,
                &ioOutputDataPacketSize,
                &outBufferList,
                nil
            )

            if status == noErr || status == 100 /* done */ {
                let produced = Int(outBufferList.mBuffers.mDataByteSize)
                allPCM.append(contentsOf: pcmBuffer[0..<produced])
            } else if status != noErr {
                // Some frames may fail, continue with remaining
                logger.warning("AudioConverter decode frame failed: \(status)")
            }
        }

        guard !allPCM.isEmpty else { return nil }

        // Build WAV header + PCM data
        return buildWAV(pcmData: allPCM, sampleRate: UInt32(sampleRate),
                        channels: UInt16(channels), bitsPerSample: UInt16(bytesPerSample * 8))
    }

    // Input callback for AudioConverterFillComplexBuffer
    private struct OpusInputContext {
        let data: Data
        let packetDesc: AudioStreamPacketDescription
        var consumed: Bool
    }

    private static let opusInputCallback: AudioConverterComplexInputDataProc = {
        (converter, ioNumberDataPackets, ioData, outDataPacketDescription, inUserData) -> OSStatus in

        guard let ctx = inUserData?.assumingMemoryBound(to: OpusInputContext.self) else {
            ioNumberDataPackets.pointee = 0
            return -50 // paramErr
        }

        if ctx.pointee.consumed {
            ioNumberDataPackets.pointee = 0
            return 100 // done
        }

        ctx.pointee.data.withUnsafeBytes { rawBuf in
            ioData.pointee.mNumberBuffers = 1
            ioData.pointee.mBuffers.mNumberChannels = 1
            ioData.pointee.mBuffers.mDataByteSize = UInt32(rawBuf.count)
            ioData.pointee.mBuffers.mData = UnsafeMutableRawPointer(mutating: rawBuf.baseAddress!)
        }
        ioNumberDataPackets.pointee = 1

        if let descPtr = outDataPacketDescription {
            descPtr.pointee = UnsafeMutablePointer<AudioStreamPacketDescription>.allocate(capacity: 1)
            descPtr.pointee!.pointee = ctx.pointee.packetDesc
        }

        ctx.pointee.consumed = true
        return noErr
    }

    /// Build minimal WAV file from raw PCM data.
    private static func buildWAV(pcmData: Data, sampleRate: UInt32,
                                  channels: UInt16, bitsPerSample: UInt16) -> Data {
        let byteRate = sampleRate * UInt32(channels) * UInt32(bitsPerSample / 8)
        let blockAlign = channels * (bitsPerSample / 8)
        let dataSize = UInt32(pcmData.count)
        let fileSize = 36 + dataSize

        var wav = Data()
        wav.reserveCapacity(Int(fileSize + 8))

        // RIFF header
        wav.append(contentsOf: [0x52, 0x49, 0x46, 0x46]) // "RIFF"
        wav.appendLE32(fileSize)
        wav.append(contentsOf: [0x57, 0x41, 0x56, 0x45]) // "WAVE"

        // fmt chunk
        wav.append(contentsOf: [0x66, 0x6D, 0x74, 0x20]) // "fmt "
        wav.appendLE32(16)                                  // chunk size
        wav.appendLE16(1)                                   // PCM format
        wav.appendLE16(channels)
        wav.appendLE32(sampleRate)
        wav.appendLE32(byteRate)
        wav.appendLE16(blockAlign)
        wav.appendLE16(bitsPerSample)

        // data chunk
        wav.append(contentsOf: [0x64, 0x61, 0x74, 0x61]) // "data"
        wav.appendLE32(dataSize)
        wav.append(pcmData)

        return wav
    }

    // MARK: - CAF Builder

    private static func buildCAF(_ parsed: ParsedWebM) -> Data? {
        var caf = Data()
        let framesPerPacket: UInt32 = 960 // 20ms Opus frames at 48kHz

        // -- File Header --
        caf.appendBE32(0x63616666) // 'caff'
        caf.appendBE16(1)          // version
        caf.appendBE16(0)          // flags

        // -- Audio Description ('desc') --
        caf.appendBE32(0x64657363) // 'desc'
        caf.appendBE64(32)         // chunk size = sizeof(AudioStreamBasicDescription)
        caf.appendBEFloat64(parsed.sampleRate)
        caf.appendBE32(0x6F707573) // 'opus' (kAudioFormatOpus)
        caf.appendBE32(0)          // format flags
        caf.appendBE32(0)          // bytes per packet (variable)
        caf.appendBE32(framesPerPacket)
        caf.appendBE32(parsed.channels)
        caf.appendBE32(0)          // bits per channel (compressed)

        // -- Magic Cookie ('kuki') — OpusHead from WebM CodecPrivate --
        if let cp = parsed.codecPrivate, !cp.isEmpty {
            caf.appendBE32(0x6B756B69) // 'kuki'
            caf.appendBE64(Int64(cp.count))
            caf.append(cp)
        }

        // -- Packet Table ('pakt') --
        let numPackets = Int64(parsed.frames.count)
        let numValidFrames = numPackets * Int64(framesPerPacket)

        var paktBody = Data()
        paktBody.appendBE64(numPackets)
        paktBody.appendBE64(numValidFrames)
        paktBody.appendBE32S(0)  // priming frames (Int32)
        paktBody.appendBE32S(0)  // remainder frames (Int32)
        for frame in parsed.frames {
            paktBody.appendBERInt(frame.count)
        }

        caf.appendBE32(0x70616B74) // 'pakt'
        caf.appendBE64(Int64(paktBody.count))
        caf.append(paktBody)

        // -- Audio Data ('data') --
        var totalAudioSize = 0
        for frame in parsed.frames { totalAudioSize += frame.count }

        caf.appendBE32(0x64617461) // 'data'
        caf.appendBE64(Int64(totalAudioSize + 4)) // +4 for editCount
        caf.appendBE32(0)          // edit count
        for frame in parsed.frames { caf.append(frame) }

        return caf
    }

    // MARK: - CAF → M4A Transcoding

    private static func transcodeCAFtoM4A(_ cafData: Data) -> Data? {
        let tmp = FileManager.default.temporaryDirectory
        let cafURL = tmp.appendingPathComponent("webm_conv_\(UUID().uuidString).caf")
        let m4aURL = tmp.appendingPathComponent("webm_conv_\(UUID().uuidString).m4a")
        defer {
            try? FileManager.default.removeItem(at: cafURL)
            try? FileManager.default.removeItem(at: m4aURL)
        }

        do {
            try cafData.write(to: cafURL)

            let inputFile = try AVAudioFile(forReading: cafURL)
            let pcmFormat = inputFile.processingFormat

            let outputSettings: [String: Any] = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: pcmFormat.sampleRate,
                AVNumberOfChannelsKey: pcmFormat.channelCount,
                AVEncoderBitRateKey: 64000
            ]
            let outputFile = try AVAudioFile(forWriting: m4aURL, settings: outputSettings)

            let bufferCapacity: AVAudioFrameCount = 4096
            guard let buffer = AVAudioPCMBuffer(pcmFormat: pcmFormat, frameCapacity: bufferCapacity) else {
                return nil
            }

            while inputFile.framePosition < inputFile.length {
                try inputFile.read(into: buffer)
                try outputFile.write(from: buffer)
            }

            return try Data(contentsOf: m4aURL)
        } catch {
            logger.error("CAF→M4A transcode failed: \(error.localizedDescription)")
            return nil
        }
    }

    // MARK: - EBML Primitives

    private static func readVINTValue(_ bytes: [UInt8], at pos: Int) -> (UInt64, Int)? {
        guard pos < bytes.count, bytes[pos] != 0 else { return nil }
        let length = bytes[pos].leadingZeroBitCount + 1
        guard length <= 8, pos + length <= bytes.count else { return nil }

        var value = UInt64(bytes[pos]) & ((1 << (8 - length)) - 1)
        for i in 1..<length { value = (value << 8) | UInt64(bytes[pos + i]) }
        return (value, length)
    }

    private static func readElementID(_ bytes: [UInt8], at pos: Int) -> (UInt32, Int)? {
        guard pos < bytes.count, bytes[pos] != 0 else { return nil }
        let length = bytes[pos].leadingZeroBitCount + 1
        guard length <= 4, pos + length <= bytes.count else { return nil }

        var id = UInt32(bytes[pos])
        for i in 1..<length { id = (id << 8) | UInt32(bytes[pos + i]) }
        return (id, length)
    }

    private static func readUInt(_ bytes: [UInt8], at pos: Int, count: Int) -> UInt64 {
        var value: UInt64 = 0
        for i in 0..<min(count, 8) where pos + i < bytes.count {
            value = (value << 8) | UInt64(bytes[pos + i])
        }
        return value
    }

    private static func readFloat(_ bytes: [UInt8], at pos: Int, count: Int) -> Double {
        if count == 4, pos + 4 <= bytes.count {
            let bits = (UInt32(bytes[pos]) << 24) | (UInt32(bytes[pos + 1]) << 16) |
                       (UInt32(bytes[pos + 2]) << 8) | UInt32(bytes[pos + 3])
            return Double(Float(bitPattern: bits))
        } else if count == 8, pos + 8 <= bytes.count {
            var bits: UInt64 = 0
            for i in 0..<8 { bits = (bits << 8) | UInt64(bytes[pos + i]) }
            return Double(bitPattern: bits)
        }
        return 48000 // default Opus sample rate
    }
}

// MARK: - Data Little-Endian Writers (WAV format)

private extension Data {
    mutating func appendLE16(_ v: UInt16) {
        append(UInt8(v & 0xFF)); append(UInt8(v >> 8))
    }
    mutating func appendLE32(_ v: UInt32) {
        append(UInt8(v & 0xFF)); append(UInt8((v >> 8) & 0xFF))
        append(UInt8((v >> 16) & 0xFF)); append(UInt8((v >> 24) & 0xFF))
    }
}

// MARK: - Data Big-Endian Writers (CAF format)

private extension Data {
    mutating func appendBE16(_ v: UInt16) {
        append(UInt8(v >> 8)); append(UInt8(v & 0xFF))
    }
    mutating func appendBE32(_ v: UInt32) {
        append(UInt8((v >> 24) & 0xFF)); append(UInt8((v >> 16) & 0xFF))
        append(UInt8((v >> 8) & 0xFF));  append(UInt8(v & 0xFF))
    }
    mutating func appendBE32S(_ v: Int32) { appendBE32(UInt32(bitPattern: v)) }
    mutating func appendBE64(_ v: Int64) {
        let u = UInt64(bitPattern: v)
        for i in stride(from: 56, through: 0, by: -8) { append(UInt8((u >> i) & 0xFF)) }
    }
    mutating func appendBEFloat64(_ v: Double) {
        let bits = v.bitPattern
        for i in stride(from: 56, through: 0, by: -8) { append(UInt8((bits >> i) & 0xFF)) }
    }
    /// Variable-length BER integer (CAF packet table).
    mutating func appendBERInt(_ value: Int) {
        var v = value
        var encoded = [UInt8]()
        encoded.append(UInt8(v & 0x7F))
        v >>= 7
        while v > 0 {
            encoded.append(UInt8(0x80 | (v & 0x7F)))
            v >>= 7
        }
        for byte in encoded.reversed() { append(byte) }
    }
}
