package top.yeij.cyrene.voice.stt

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.util.Base64
import android.util.Log
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import top.yeij.cyrene.domain.repository.ChatRepository
import java.io.ByteArrayOutputStream

class BackendSttProvider(
    private val context: Context,
    private val chatRepository: ChatRepository,
) : SttProvider {

    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private var audioRecord: AudioRecord? = null
    private var recordingJob: Job? = null
    private val audioBuffer = ByteArrayOutputStream()

    private val _partialResult = MutableStateFlow("")
    override val partialResult: StateFlow<String> = _partialResult.asStateFlow()

    private val _finalResult = MutableSharedFlow<SttResult>(extraBufferCapacity = 8)
    override val finalResult: SharedFlow<SttResult> = _finalResult.asSharedFlow()

    private val _onError = MutableSharedFlow<String>(extraBufferCapacity = 8)
    override val onError: SharedFlow<String> = _onError.asSharedFlow()

    private val _isListening = MutableStateFlow(false)
    override val isListening: StateFlow<Boolean> = _isListening.asStateFlow()

    private val sampleRate = 16000
    private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
    private val channelConfig = AudioFormat.CHANNEL_IN_MONO
    private val bufferSize: Int = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
        .coerceAtLeast(3200)

    override fun start() {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED
        ) {
            _onError.tryEmit("缺少录音权限")
            return
        }

        cancel()
        _isListening.value = true
        _partialResult.value = ""
        audioBuffer.reset()

        try {
            audioRecord = AudioRecord(
                MediaRecorder.AudioSource.VOICE_RECOGNITION,
                sampleRate,
                channelConfig,
                audioFormat,
                bufferSize,
            ).also {
                if (it.state != AudioRecord.STATE_INITIALIZED) {
                    Log.e(TAG, "AudioRecord init failed")
                    _onError.tryEmit("麦克风初始化失败")
                    _isListening.value = false
                    return
                }
                it.startRecording()
            }

            val readBuffer = ByteArray(bufferSize)
            recordingJob = scope.launch {
                while (isActive && _isListening.value) {
                    val bytesRead = audioRecord?.read(readBuffer, 0, readBuffer.size) ?: -1
                    if (bytesRead > 0) {
                        audioBuffer.write(readBuffer, 0, bytesRead)
                    } else if (bytesRead < 0) break
                }
            }
        } catch (e: SecurityException) {
            _onError.tryEmit("缺少录音权限")
            _isListening.value = false
        } catch (e: Exception) {
            Log.e(TAG, "Recording error", e)
            _onError.tryEmit("录音失败: ${e.message}")
            _isListening.value = false
        }
    }

    override fun stop() {
        if (!_isListening.value) return
        Log.d(TAG, "Stopping recording")
        stopRecording()

        val audioBytes = audioBuffer.toByteArray()
        if (audioBytes.isEmpty()) {
            Log.w(TAG, "No audio data recorded")
            _isListening.value = false
            _onError.tryEmit("未录制到语音")
            return
        }

        _isListening.value = false
        scope.launch {
            try {
                val base64 = Base64.encodeToString(audioBytes, Base64.NO_WRAP)
                chatRepository.sendVoiceInput(base64, "voice_msg")
                Log.d(TAG, "Sent ${audioBytes.size} bytes of audio to backend")
            } catch (e: Exception) {
                Log.e(TAG, "Failed to send voice input", e)
                _onError.tryEmit("发送语音失败: ${e.message}")
            }
        }
    }

    override fun cancel() {
        if (!_isListening.value) return
        Log.d(TAG, "Cancelling recording")
        stopRecording()
        audioBuffer.reset()
        _isListening.value = false
        _partialResult.value = ""
    }

    private fun stopRecording() {
        recordingJob?.cancel()
        recordingJob = null
        try {
            audioRecord?.stop()
            audioRecord?.release()
        } catch (e: Exception) {
            Log.w(TAG, "Error releasing AudioRecord: ${e.message}")
        }
        audioRecord = null
    }

    fun shutdown() {
        cancel()
        scope.cancel()
    }

    companion object {
        private const val TAG = "BackendSTT"
    }
}
