package top.yeij.cyrene.util

import android.content.Context
import android.media.MediaRecorder
import android.os.Build
import android.util.Base64
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.File

enum class RecordState {
    IDLE,
    RECORDING,
    LOCKED,  // hands-free mode
    CANCELLING,
}

class VoiceRecorder(private val context: Context) {

    private var recorder: MediaRecorder? = null
    private var outputFile: File? = null
    private var timerJob: Job? = null

    private val _state = MutableStateFlow(RecordState.IDLE)
    val state: StateFlow<RecordState> = _state.asStateFlow()

    private val _durationMs = MutableStateFlow(0L)
    val durationMs: StateFlow<Long> = _durationMs.asStateFlow()

    fun start() {
        if (_state.value != RecordState.IDLE) return
        try {
            outputFile = File(context.cacheDir, "voice_${System.currentTimeMillis()}.aac")
            outputFile?.delete()

            recorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                MediaRecorder(context)
            } else {
                @Suppress("DEPRECATION")
                MediaRecorder()
            }

            recorder?.apply {
                setAudioSource(MediaRecorder.AudioSource.MIC)
                setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS)
                setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
                setAudioSamplingRate(16000)
                setAudioEncodingBitRate(32000)
                setAudioChannels(1)
                setOutputFile(outputFile?.absolutePath)
                prepare()
                start()
            }

            _state.value = RecordState.RECORDING
            startTimer()
            Log.i(TAG, "Recording started: ${outputFile?.absolutePath}")
        } catch (e: Exception) {
            Log.e(TAG, "Failed to start recording: ${e.message}", e)
            cleanup()
        }
    }

    fun lock() {
        if (_state.value == RecordState.RECORDING) {
            _state.value = RecordState.LOCKED
        }
    }

    fun stop(): File? {
        if (_state.value == RecordState.IDLE) return null
        val file = outputFile
        try {
            recorder?.apply {
                try { stop() } catch (_: Exception) { }
                try { release() } catch (_: Exception) { }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error stopping recorder: ${e.message}", e)
        }
        recorder = null
        cancelTimer()
        _state.value = RecordState.IDLE
        _durationMs.value = 0L
        Log.i(TAG, "Recording stopped: ${file?.absolutePath}, size=${file?.length()}")
        return if (file != null && file.exists() && file.length() > 0) file else null
    }

    fun cancel() {
        val file = outputFile
        try {
            recorder?.apply {
                try { stop() } catch (_: Exception) { }
                try { release() } catch (_: Exception) { }
            }
        } catch (e: Exception) { }
        recorder = null
        file?.delete()
        cancelTimer()
        _state.value = RecordState.IDLE
        _durationMs.value = 0L
        Log.i(TAG, "Recording cancelled")
    }

    fun cleanup() {
        try { recorder?.release() } catch (_: Exception) { }
        recorder = null
        outputFile?.delete()
        outputFile = null
        cancelTimer()
        _state.value = RecordState.IDLE
        _durationMs.value = 0L
    }

    fun getBase64(): String? {
        val file = outputFile ?: return null
        if (!file.exists()) return null
        return try {
            val bytes = file.readBytes()
            Base64.encodeToString(bytes, Base64.NO_WRAP)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to encode audio: ${e.message}", e)
            null
        }
    }

    fun deleteFile() {
        outputFile?.delete()
        outputFile = null
    }

    private fun startTimer() {
        cancelTimer()
        val startTime = System.currentTimeMillis()
        timerJob = CoroutineScope(Dispatchers.Main).launch {
            while (true) {
                delay(100)
                _durationMs.value = System.currentTimeMillis() - startTime
            }
        }
    }

    private fun cancelTimer() {
        timerJob?.cancel()
        timerJob = null
    }

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