package com.adealink.weparty.im.session.comp

import android.annotation.SuppressLint
import androidx.lifecycle.LifecycleOwner
import com.adealink.frame.aab.util.getCompatColor
import com.adealink.frame.log.Log
import com.adealink.frame.mvvm.view.ViewComponent
import com.adealink.frame.mvvm.viewmodel.activityViewModels
import com.adealink.frame.util.onClick
import com.adealink.frame.util.runOnUiThread
import com.adealink.weparty.commonui.ext.dpf
import com.adealink.weparty.commonui.toast.util.showToast
import com.adealink.weparty.im.R
import com.adealink.weparty.im.databinding.LayoutSessionBottomVoiceBarBinding
import com.adealink.weparty.im.session.comp.input.AudioInputStatus
import com.adealink.weparty.im.session.comp.input.InputAction
import com.adealink.weparty.im.session.comp.input.InputState
import com.adealink.weparty.im.session.comp.viewmodel.SessionAudioViewModel
import com.adealink.weparty.im.session.comp.viewmodel.SessionInputViewModel
import com.adealink.weparty.module.im.data.TAG_IM_AUDIO
import com.tencent.qcloud.tuicore.TUIConstants
import com.tencent.qcloud.tuikit.tuichat.bean.ChatInfo
import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioPlayer
import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioRecorder
import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioRecorder.AudioRecorderCallback
import com.tencent.qcloud.tuikit.tuichat.presenter.ChatPresenter
import com.tencent.qcloud.tuikit.tuichat.util.ChatMessageBuilder
import java.util.Timer
import java.util.TimerTask
import kotlin.math.max
import kotlin.math.pow
import com.adealink.weparty.R as APP_R

@SuppressLint("SetTextI18n")
class SessionBottomAudioComp(
    lifecycleOwner: LifecycleOwner,
    val voiceBar: LayoutSessionBottomVoiceBarBinding,
    val chatInfo: ChatInfo? = null,
    val presenter: ChatPresenter? = null
) : ViewComponent(lifecycleOwner) {

    companion object {
        private const val TAG = "SessionBottomAudioComp"
        private const val MIN_VOICE_DB = 2f
    }

    private var mTimer: Timer? = null
    private var times = 0
    private val inputViewModel by activityViewModels<SessionInputViewModel>()
    private val audioViewModel by activityViewModels<SessionAudioViewModel>()

    private var isPause = false
    override fun onCreate() {
        super.onCreate()
        initView()
        observeViewModel()
    }


    private fun initView() {
        voiceBar.btnCancel.onClick {
            clickCancel()
        }
        voiceBar.btnPause.onClick {
            clickPause()
        }
        voiceBar.btnSend.onClick {
            clickSend()
        }
    }

    private fun observeViewModel() {
        /**
         * transition to [InputState.STATE_AUDIO_INPUT]
         */
        inputViewModel.registerTransaction(
            TAG,
            currentState = InputState.STATE_SOFT_INPUT,
            action = InputAction.CLICK_AUDIO,
            nextState = InputState.STATE_AUDIO_INPUT
        ).event.observeWithoutCache(viewLifecycleOwner) {
            startAudioRecord()
        }
        inputViewModel.registerTransaction(
            TAG,
            currentState = InputState.STATE_EMOJI_INPUT,
            action = InputAction.CLICK_AUDIO,
            nextState = InputState.STATE_AUDIO_INPUT
        ).event.observeWithoutCache(viewLifecycleOwner) {
            startAudioRecord()
        }
        inputViewModel.registerTransaction(
            TAG,
            currentState = InputState.STATE_NONE,
            action = InputAction.CLICK_AUDIO,
            nextState = InputState.STATE_AUDIO_INPUT
        ).event.observeWithoutCache(viewLifecycleOwner) {
            startAudioRecord()
        }


        inputViewModel.registerTransaction(
            TAG,
            currentState = InputState.STATE_AUDIO_INPUT,
            action = InputAction.CLICK_AUDIO,
            nextState = InputState.STATE_NONE
        ).event.observeWithoutCache(viewLifecycleOwner) {
            stopAudioRecord()
        }
        inputViewModel.registerTransaction(
            TAG,
            currentState = InputState.STATE_AUDIO_INPUT,
            action = InputAction.CANCEL_AUDIO,
            nextState = InputState.STATE_NONE
        ).event.observeWithoutCache(viewLifecycleOwner) {
            cancelAudioRecord()
        }

        /**
         * transition from [InputState.STATE_AUDIO_INPUT]
         */
        inputViewModel.inputStateLD.observe(viewLifecycleOwner) { state ->
            if (state.lastState == InputState.STATE_AUDIO_INPUT
                && state.currentState != InputState.STATE_AUDIO_INPUT
            ) {
                stopAudioRecord()
            }
        }


        audioViewModel.audioInputStatusLD.observeWithoutCache(viewLifecycleOwner) { status ->
//            when (status) {
//                AudioInputStatus.RECORD_CONTINUE -> TODO()
//                AudioInputStatus.RECORD_FAILED -> TODO()
//                AudioInputStatus.RECORD_START -> TODO()
//                AudioInputStatus.RECORD_STOP -> TODO()
//                AudioInputStatus.RECORD_READY_TO_CANCEL -> TODO()
//                AudioInputStatus.RECORD_TOO_SHORT -> {
//
//                }
//                AudioInputStatus.RECORD_CANCEL -> TODO()
//            }
        }
    }

    private fun clickCancel() {
        inputViewModel.execute(InputAction.CANCEL_AUDIO)
    }

    private fun clickPause() {
        if (isPause) {
            isPause = false
            voiceBar.btnPause.setImageResource(R.drawable.im_session_voice_record_pause_ic)
            resumeAudioRecord()
        } else {
            isPause = true
            voiceBar.btnPause.setImageResource(R.drawable.im_session_voice_record_resume_ic)
            pauseAudioRecord()
        }
    }

    private fun clickSend() {
        inputViewModel.execute(InputAction.CLICK_AUDIO)
    }

    private fun startAudioRecord() {
        //录音前关闭音频播放
        AudioPlayer.getInstance().stopPlay()

        Log.d(TAG_IM_AUDIO, "startAudioRecord")
        AudioRecorder.startRecord(object : AudioRecorderCallback {
            override fun onStarted() {
                Log.d(TAG_IM_AUDIO, "startAudioRecord, onStarted")
                audioViewModel.changeStatus(AudioInputStatus.RECORD_START)
                showVoiceLayout()
                if (mTimer == null) {
                    mTimer = Timer()
                }
                mTimer?.schedule(object : TimerTask() {
                    override fun run() {
                        runOnUiThread {
                            if (!isPause) {
                                times++
                                voiceBar.tvRecordTime.text = formatMiss(times)
                            }
                        }
                    }
                }, 0, 1000)
            }

            override fun onFinished(outputPath: String?) {
                Log.d(TAG_IM_AUDIO, "startAudioRecord, onFinished:$outputPath")
                hideVoiceLayout()
                val duration = AudioRecorder.getDuration(outputPath)
                if (duration < 1000) {
                    showToast(R.string.im_audio_say_time_short)
                    audioViewModel.changeStatus(AudioInputStatus.RECORD_TOO_SHORT)
                    return
                }
                audioViewModel.changeStatus(AudioInputStatus.RECORD_STOP)
                voiceBar.vVoiceWave.stop()
                sendAudioMessage(outputPath, duration)
            }

            override fun onFailed(errorCode: Int, errorMessage: String?) {
                Log.d(
                    TAG_IM_AUDIO,
                    "startAudioRecord, onFailed, errorCode:$errorCode, errorMessage:$errorMessage"
                )
                hideVoiceLayout()
                if (errorCode == AudioRecorder.ERROR_CODE_MIC_IS_BEING_USED || errorCode == TUIConstants.TUICalling.ERROR_STATUS_IN_CALL) {
                    showToast(R.string.im_mic_is_being_used_cant_record)
                } else {
                    showToast(R.string.im_record_audio_failed)
                }
                Log.e(
                    TAG_IM_AUDIO,
                    "record audio failed, errorCode $errorCode, errorMessage $errorMessage"
                )
                audioViewModel.changeStatus(AudioInputStatus.RECORD_FAILED)
                voiceBar.vVoiceWave.stop()
                inputViewModel.execute(InputAction.EMPTY_CLICKED)
            }

            override fun onCanceled() {
                Log.d(TAG_IM_AUDIO, "startAudioRecord, onCanceled")
                hideVoiceLayout()
                audioViewModel.changeStatus(AudioInputStatus.RECORD_CANCEL)
                voiceBar.vVoiceWave.stop()
            }

            override fun onVoiceDb(db: Double) {
                if (isPause) {
                    return
                }
                Log.d(TAG_IM_AUDIO, "startAudioRecord, onVoiceDb: $db")
                voiceBar.vVoiceWave.updateDecibel(
                    max(
                        soundWaveDbInterpolator(db).toFloat(), MIN_VOICE_DB
                    )
                )
                voiceBar.vVoiceWave.start()
            }
        })
    }

    private fun pauseAudioRecord() {
        Log.d(TAG_IM_AUDIO, "pauseAudioRecord")
        voiceBar.vVoiceWave.pause()
        AudioRecorder.pauseRecord()
    }

    private fun resumeAudioRecord() {
        Log.d(TAG_IM_AUDIO, "resumeAudioRecord")
        voiceBar.vVoiceWave.resume()
        AudioRecorder.resumeRecord()
    }

    private fun hideVoiceLayout() {
        Log.d(TAG_IM_AUDIO, "hideVoiceLayout")
        resetVoiceView()
    }

    private fun showVoiceLayout() {
        Log.d(TAG_IM_AUDIO, "showVoiceLayout")
        initVoiceWaveView()
    }

    private fun initVoiceWaveView() {
        voiceBar.vVoiceWave.setColorRange(
            getCompatColor(APP_R.color.color_FF4E5969),
            getCompatColor(APP_R.color.color_FF4E5969),
            getCompatColor(APP_R.color.color_FF4E5969)
        )
        voiceBar.vVoiceWave.barSpacing = 2.dpf()
        voiceBar.vVoiceWave.showGlow = false
        voiceBar.vVoiceWave.clear()
    }

    private fun stopAudioRecord() {
        Log.d(TAG_IM_AUDIO, "stopAudioRecord")
        AudioRecorder.stopRecord()
        voiceBar.vVoiceWave.stop()
        voiceBar.vVoiceWave.clear()
    }

    private fun cancelAudioRecord() {
        Log.d(TAG_IM_AUDIO, "cancelAudioRecord")
        AudioRecorder.cancelRecord()
    }

    private fun resetVoiceView() {
        Log.d(TAG_IM_AUDIO, "resetVoiceView")
        mTimer?.cancel()
        mTimer = null
        voiceBar.tvRecordTime.text = "0:00"
        times = 0
    }

    private fun sendAudioMessage(outputPath: String?, duration: Int) {
        Log.d(TAG_IM_AUDIO, "sendAudioMessage, outputPath:$outputPath, duration:$duration")
        inputViewModel.sendMessage(ChatMessageBuilder.buildAudioMessage(outputPath, duration))
    }

    // TODO: 手指操作
//    private fun readyToCancelRecord() {
//        if (mChatInputHandler != null) {
//            mChatInputHandler.onRecordStatusChanged(InputView.ChatInputHandler.RECORD_READY_TO_CANCEL)
//        }
//        voiceDeleteImage.setBackgroundResource(R.drawable.minimalist_delete_start_icon)
//        voiceDeleteImage.getBackground().setAutoMirrored(true)
//        mSendAudioButton.setBackground(getResources().getDrawable(R.drawable.minimalist_corner_bg_red))
//    }
//
//    private fun continueRecord() {
//        if (mChatInputHandler != null) {
//            mChatInputHandler.onRecordStatusChanged(InputView.ChatInputHandler.RECORD_CONTINUE)
//        }
//        voiceDeleteImage.setBackgroundResource(R.drawable.minimalist_delete_icon)
//        mSendAudioButton.setBackground(getResources().getDrawable(R.drawable.minimalist_corner_bg_blue))
//    }

    private fun formatMiss(miss: Int): String {
        // String hh = miss / 3600 > 9 ? miss / 3600 + "" : "0" + miss / 3600;
        val mm =
            if ((miss % 3600) / 60 > 9) ((miss % 3600) / 60).toString() + "" else "0" + (miss % 3600) / 60
        val ss =
            if ((miss % 3600) % 60 > 9) ((miss % 3600) % 60).toString() + "" else "0" + (miss % 3600) % 60
        return "$mm:$ss"
    }

    /**
     * 声音分贝数值(0-100)
     */
    private fun soundWaveDbInterpolator(db: Double): Double {
        val dbp = db / 100.0
        val factor = 2.0
        return db * dbp.pow(factor)
    }
}