package com.lila.wakeup.kws

import android.annotation.SuppressLint
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.util.Log
import com.lila.wakeup.Config

/**
 * 麦克风音频采集封装。
 *
 * 设计要点:
 *  - AudioSource 用 VOICE_RECOGNITION:启用 Android 系统 AGC + NS(自动增益 + 降噪)
 *    板载 mic 信号弱(说话峰值 16000 vs 底噪 5000,SNR ~10dB),用 MIC raw 信号
 *    经 SenseVoice 识别后输出全空。VOICE_RECOGNITION 走系统 ASR 标准路径,硬件 AGC
 *    把弱信号拉到合理量程,实测 ASR 命中率显著提升。
 *  - 16kHz / mono / 16-bit PCM(与 sherpa-onnx 模型严格对齐)
 *  - 100ms 帧(1600 sample),过小帧会导致 sherpa-onnx 内部 chunk 边界异常
 *  - 暂停时完全 release AudioRecord,释放麦克风给 RTC SDK
 *
 * 注:这是常驻读取线程,不是 ForegroundService 主线程。
 */
class AudioCapture(
    /** 每读到一帧 PCM 数据时回调，参数为 16-bit PCM short[] */
    private val onFrame: (ShortArray) -> Unit
) {
    companion object {
        private const val TAG = "KwsService.Audio"
    }

    @Volatile private var record: AudioRecord? = null
    @Volatile private var thread: Thread? = null
    @Volatile private var running = false

    @SuppressLint("MissingPermission")  // 权限由 MainActivity 运行时申请
    fun start() {
        if (running) {
            Log.w(TAG, "start() called while already running, skip")
            return
        }

        val minBuf = AudioRecord.getMinBufferSize(
            Config.SAMPLE_RATE, Config.CHANNEL_CONFIG, Config.ENCODING
        )
        // ring buffer 必须 >= 一帧字节数,否则 read(1600) 在 1280 字节 buffer 上读不全,
        // 数据断帧导致 VAD/ASR 收到的是部分 + 历史脏数据。取 max(minBuf*2, frameBytes*4)
        // 给 100ms 帧充足缓冲,同时不丢帧。
        val frameBytes = Config.FRAME_SAMPLES * 2  // 16-bit PCM = 2 字节/样本
        val bufSize = maxOf(minBuf * 2, frameBytes * 4)

        record = AudioRecord.Builder()
            .setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION)   // 启用系统 AGC/NS
            .setAudioFormat(
                AudioFormat.Builder()
                    .setSampleRate(Config.SAMPLE_RATE)
                    .setChannelMask(Config.CHANNEL_CONFIG)
                    .setEncoding(Config.ENCODING)
                    .build()
            )
            .setBufferSizeInBytes(bufSize)
            .build()

        if (record?.state != AudioRecord.STATE_INITIALIZED) {
            Log.e(TAG, "AudioRecord init failed, state=${record?.state}")
            release()
            return
        }

        record?.startRecording()
        running = true
        Log.i(TAG, "AudioRecord started, bufSize=$bufSize bytes")

        thread = Thread {
            val frame = ShortArray(Config.FRAME_SAMPLES)
            val gain = Config.AUDIO_CAPTURE_GAIN
            var frameCount = 0L
            while (running) {
                val n = record?.read(frame, 0, frame.size) ?: 0
                if (n > 0) {
                    // 软件预增益: 板载 mic 信号偏弱,放大让 KWS 看到更显著特征
                    if (gain != 1.0f) {
                        for (i in 0 until n) {
                            val v = (frame[i] * gain).toInt()
                            frame[i] = when {
                                v > 32767 -> 32767
                                v < -32768 -> -32768
                                else -> v.toShort()
                            }
                        }
                    }
                    // 每 10 帧 (1 秒) 打印一次音量峰值
                    if (++frameCount % 10 == 0L) {
                        var peak = 0
                        for (i in 0 until n) {
                            val v = if (frame[i] >= 0) frame[i].toInt() else -frame[i].toInt()
                            if (v > peak) peak = v
                        }
                        Log.i(TAG, "[Audio] frame#$frameCount n=$n peak=$peak (gain=${gain}x)")
                    }
                    onFrame(frame)
                } else if (n < 0) {
                    Log.e(TAG, "AudioRecord.read error code=$n, abort")
                    break
                }
            }
            Log.i(TAG, "AudioCapture thread exit")
        }.apply {
            name = "KwsAudioCapture"
            priority = Thread.NORM_PRIORITY + 2   // 略高于普通线程
            isDaemon = true
            start()
        }
    }

    fun stop() {
        if (!running) return
        running = false
        thread?.join(500)
        thread = null
        release()
        Log.i(TAG, "AudioRecord stopped")
    }

    private fun release() {
        record?.let {
            try {
                if (it.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
                    it.stop()
                }
                it.release()
            } catch (e: Exception) {
                Log.e(TAG, "release failed: ${e.message}")
            }
        }
        record = null
    }

    fun isRunning(): Boolean = running
}
