/**
 * SILK → WAV transcoding for WeChat voice messages.
 *
 * WeChat ships voice as Tencent's SILK v3 codec (a Skype SILK variant).
 * Multimodal LLMs (Whisper / Gemini / GPT-4o audio) cannot consume `.silk`
 * directly, so we decode it into PCM s16le + wrap with a WAV header.
 *
 * Failure mode: if the wasm decoder is unavailable or input is not valid SILK,
 * `silkToWav` returns null and the caller should fall back to the raw bytes.
 */

/** Default sample rate for WeChat voice messages. */
const SILK_SAMPLE_RATE = 24_000

/**
 * Wrap raw pcm_s16le bytes in a minimal RIFF/WAVE container.
 * Mono channel, 16-bit signed little-endian.
 */
function pcmBytesToWav(pcm: Uint8Array, sampleRate: number): Buffer {
    const pcmBytes = pcm.byteLength
    const totalSize = 44 + pcmBytes
    const buf = Buffer.allocUnsafe(totalSize)
    let offset = 0

    buf.write('RIFF', offset); offset += 4
    buf.writeUInt32LE(totalSize - 8, offset); offset += 4
    buf.write('WAVE', offset); offset += 4

    buf.write('fmt ', offset); offset += 4
    buf.writeUInt32LE(16, offset); offset += 4              // fmt chunk size
    buf.writeUInt16LE(1, offset); offset += 2               // PCM format
    buf.writeUInt16LE(1, offset); offset += 2               // mono
    buf.writeUInt32LE(sampleRate, offset); offset += 4
    buf.writeUInt32LE(sampleRate * 2, offset); offset += 4  // byte rate (mono 16-bit)
    buf.writeUInt16LE(2, offset); offset += 2               // block align
    buf.writeUInt16LE(16, offset); offset += 2              // bits per sample

    buf.write('data', offset); offset += 4
    buf.writeUInt32LE(pcmBytes, offset); offset += 4

    Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength).copy(buf, offset)
    return buf
}

/**
 * Try to transcode a SILK audio buffer to WAV.
 *
 * Returns:
 *   - WAV Buffer on success
 *   - null when silk-wasm is unavailable, input is malformed, or decoding fails
 *
 * Callers should fall back to the raw silk bytes when null is returned.
 */
export async function silkToWav(silkBuf: Buffer): Promise<Buffer | null> {
    try {
        // Lazy import: keeps the wasm out of the cold-start path for
        // installations that never receive voice messages.
        const silkWasm = await import('silk-wasm')
        const decode = (silkWasm as { decode?: typeof silkWasm.decode }).decode
        const isSilk = (silkWasm as { isSilk?: (b: Uint8Array) => boolean }).isSilk
        if (typeof decode !== 'function') {
            console.warn('[Weixin] silkToWav: silk-wasm.decode not available')
            return null
        }
        if (typeof isSilk === 'function' && !isSilk(silkBuf)) {
            // Already wav/mp3/etc — no transcode needed.
            return null
        }

        const result = await decode(silkBuf, SILK_SAMPLE_RATE)
        return pcmBytesToWav(result.data, SILK_SAMPLE_RATE)
    } catch (err) {
        console.warn(
            '[Weixin] silkToWav: transcode failed, falling back to raw silk:',
            err instanceof Error ? err.message : err,
        )
        return null
    }
}
