package com.humand.demo.modules.AudioRecorder

import android.Manifest
import android.content.pm.PackageManager
import android.media.MediaRecorder
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import java.io.File
import java.util.UUID
import android.media.AudioFormat
import android.media.AudioRecord
import android.os.Handler
import android.os.Looper
import androidx.annotation.RequiresPermission
import androidx.core.content.ContextCompat
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import java.io.*
import java.util.Timer
import java.util.TimerTask
import com.facebook.react.modules.core.DeviceEventManagerModule


class AudioRecorderModule(private val reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {
    private var audioRecord: AudioRecord? = null
    private var recordingThread: Thread? = null
    private var isRecording = false
    private var isPaused = false
    private var isStopping = false
    private var recordingPath: String? = null
    private var copiedPath: String? = null
    private val sampleRate = 44100
    private val channelConfig = AudioFormat.CHANNEL_IN_MONO
    private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
    private val bitsPerSample = 16
    private val channelCount = 1
    private val bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)

    private var timer: Timer? = null
    private val mainHandler = Handler(Looper.getMainLooper())
    @Volatile private var currentMeteringLevel: Double = 0.0

    override fun getName() = "AudioRecorderModule"

    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
    @ReactMethod
    fun record(promise: Promise) {
        try {
            if (ContextCompat.checkSelfPermission(reactContext.applicationContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
                promise.reject("permission_denied", "Microphone permission is required to record audio")
                return
            }

            val uuid = UUID.randomUUID().toString()
            val fileName = "recording_${uuid}.pcm"
            val outputDir = reactContext.cacheDir
            val pcmFile = File(outputDir, fileName)

            val wavFileName = "recording_${uuid}_copy.wav"
            val wavFile = File(outputDir, wavFileName)

            recordingPath = pcmFile.absolutePath
            copiedPath = wavFile.absolutePath

            audioRecord = AudioRecord(
                MediaRecorder.AudioSource.MIC,
                sampleRate,
                channelConfig,
                audioFormat,
                bufferSize
            )

            isRecording = true
            isPaused = false

            audioRecord!!.startRecording()
            startTracker()

            recordingThread = Thread {
                val buffer = ByteArray(bufferSize)

                FileOutputStream(pcmFile, true).use { outputStream ->
                    while (isRecording) {
                        if (!isPaused) {
                            val read = audioRecord?.read(buffer, 0, buffer.size) ?: 0
                            if (read > 0) {
                                outputStream.write(buffer, 0, read)
                                // Calculate RMS from PCM 16-bit samples for metering
                                val sampleCount = read / 2
                                var sum = 0.0
                                for (i in 0 until sampleCount) {
                                    val low = buffer[i * 2].toInt() and 0xFF
                                    val high = buffer[i * 2 + 1].toInt()
                                    val sample = (high shl 8 or low).toShort().toDouble()
                                    sum += sample * sample
                                }
                                val rms = Math.sqrt(sum / sampleCount)
                                currentMeteringLevel = (rms / 32768.0).coerceIn(0.0, 1.0)
                            }
                        } else {
                            Thread.sleep(50)
                        }
                    }
                }
            }

            recordingThread!!.start()

            promise.resolve(copiedPath)

        } catch (e: Throwable) {
            promise.reject("recording_error", "Failed to start recording", e)
        }
    }

    @ReactMethod
    fun pause(promise: Promise) {
        val pcmPathLocal = recordingPath
        val wavPathLocal = copiedPath

        if (!isRecording || pcmPathLocal.isNullOrBlank() || wavPathLocal.isNullOrBlank()) {
            promise.reject("pause_error", "Nothing to pause")
            return
        }

        isPaused = true
        stopTracker()
        Thread {
            try {
                val pcmFile = File(pcmPathLocal)
                val wavFile = File(wavPathLocal)
                if (wavFile.exists()) {
                    wavFile.delete()
                }

                pcmToWav(pcmFile, wavFile)
                promise.resolve(wavPathLocal)
            } catch (e: Throwable) {
                promise.reject("pause_error", "Failed to create WAV copy", e)
            }
        }.start()
    }

    @ReactMethod
    fun resume(promise: Promise) {
        if (!isRecording) {
            promise.reject("resume_error", "Not recording")
            return
        }
        isPaused = false
        startTracker()
        promise.resolve(null)
    }

    @ReactMethod
    fun stop(promise: Promise) {
        val pcmPathLocal = recordingPath
        val wavPathLocal = copiedPath

        if (!isRecording || pcmPathLocal.isNullOrBlank() || wavPathLocal.isNullOrBlank() || audioRecord == null || recordingThread == null) {
            promise.reject("stop_error", "Not recording")
            return
        }

        if(isStopping){
            promise.reject("stop_in_progress", "Stop is already in progress")
            return
        }
        isStopping = true

        stopTracker()

        Thread {
            isRecording = false
            recordingThread?.join(300) // wait for the recording thread to finish
            try {
                audioRecord!!.stop()
                audioRecord!!.release()
                audioRecord = null

                // Final WAV conversion
                val pcmFile = File(pcmPathLocal)
                val wavFile = File(wavPathLocal)
                pcmToWav(pcmFile, wavFile)

                val duration = getDurationHandler()

                val result: WritableMap = Arguments.createMap().apply {
                    putString("path", wavPathLocal)
                    putDouble("duration", duration)
                }

                recordingThread = null
                recordingPath = null
                copiedPath = null

                promise.resolve(result)
            } catch (e: Throwable) {
                promise.reject("stop_error", "Failed to stop recording", e)
            }
            finally {
                isPaused = false
                isStopping = false
            }
        }.start()
    }

    @ReactMethod
    fun getDuration(promise: Promise) {
        Thread {
            promise.resolve(getDurationHandler())
        }.start()
    }

    private fun getDurationHandler(): Double {
        try {
        val file = File(recordingPath ?: "")
        if (!file.exists()) {
            return 0.0
        }

        val durationMs = calculatePcmDurationMs(file)

        return durationMs.toDouble()
        } catch (e: Throwable) {
            return  0.0
        }
    }

    private fun pcmToWav(pcmFile: File, wavFile: File) {
        val pcmData = pcmFile.readBytes()
        val wavOut = FileOutputStream(wavFile)

        val byteRate = sampleRate * channelCount * bitsPerSample / 8
        val blockAlign = (channelCount * bitsPerSample) / 8

        // WAV Header
        wavOut.write("RIFF".toByteArray())
        wavOut.write(intToLittleEndian(36 + pcmData.size)) // ChunkSize = 36 + Subchunk2Size
        wavOut.write("WAVE".toByteArray())
        wavOut.write("fmt ".toByteArray())
        wavOut.write(intToLittleEndian(16)) // Subchunk1Size (PCM format = 16)
        wavOut.write(shortToLittleEndian(1)) // AudioFormat = PCM
        wavOut.write(shortToLittleEndian(channelCount)) // NumChannels
        wavOut.write(intToLittleEndian(sampleRate)) // SampleRate
        wavOut.write(intToLittleEndian(byteRate)) // ByteRate
        wavOut.write(shortToLittleEndian(blockAlign)) // BlockAlign
        wavOut.write(shortToLittleEndian(bitsPerSample)) // BitsPerSample
        wavOut.write("data".toByteArray())
        wavOut.write(intToLittleEndian(pcmData.size)) // Subchunk2Size
        wavOut.write(pcmData)

        wavOut.close()
    }

    private fun intToLittleEndian(value: Int): ByteArray {
        return byteArrayOf(
            (value and 0xff).toByte(),
            ((value shr 8) and 0xff).toByte(),
            ((value shr 16) and 0xff).toByte(),
            ((value shr 24) and 0xff).toByte()
        )
    }

    private fun shortToLittleEndian(value: Int): ByteArray {
        return byteArrayOf(
            (value and 0xff).toByte(),
            ((value shr 8) and 0xff).toByte()
        )
    }

    // Calculate file duration by size
    private fun calculatePcmDurationMs(pcmFile: File): Long {
        val fileSizeInBytes = pcmFile.length()
        val byteRate = sampleRate * channelCount * bitsPerSample / 8

        return if (byteRate > 0) (1000L * fileSizeInBytes) / byteRate else 0L
    }

    private fun startTracker() {
        timer = Timer()
        timer?.schedule(object : TimerTask() {
            override fun run() {
                val durationMs = calculatePcmDurationMs(File(recordingPath ?: return))
                val params: WritableMap = Arguments.createMap().apply {
                    putInt("duration", durationMs.toInt())
                    putDouble("metering", currentMeteringLevel)
                }
                mainHandler.post {
                    sendEvent("audioRecordingUpdate", params)
                }
            }
        }, 0, 50)
    }

    private fun stopTracker() {
        timer?.cancel()
        timer = null
        currentMeteringLevel = 0.0
    }

    private fun sendEvent(eventName: String, params: WritableMap) {
        reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            .emit(eventName, params)
    }

    override fun invalidate() {
        try {
            stopTracker()
            isRecording = false
            isPaused = false
            recordingThread?.join(500)
            audioRecord?.stop()
            audioRecord?.release()
            audioRecord = null
            recordingPath = null
            copiedPath = null
        } catch (_: Throwable) { }
        super.invalidate()
    }
}
