package com.adealink.weparty.module.playmate.widget

import android.annotation.SuppressLint
import android.content.Context
import android.media.MediaPlayer
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.withStyledAttributes
import androidx.core.view.updateLayoutParams
import com.adealink.frame.base.IError
import com.adealink.frame.download.listener.TaskListener
import com.adealink.frame.download.task.Task
import com.adealink.frame.log.Log
import com.adealink.frame.oss.data.UploadFile
import com.adealink.frame.storageService
import com.adealink.frame.util.md5
import com.adealink.frame.util.onClick
import com.adealink.frame.util.runOnUiThread
import com.adealink.weparty.App
import com.adealink.weparty.R
import com.adealink.weparty.commonui.ext.show
import com.adealink.weparty.databinding.LayoutSoundViewBinding
import com.adealink.weparty.module.profile.data.VoiceData
import com.adealink.weparty.storage.file.FilePath.audioPath
import com.adealink.weparty.util.getDuration
import com.adealink.weparty.util.getFilePathBy
import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioPlayer
import kotlin.math.max

@SuppressLint("SetTextI18n")
class SoundView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    companion object {
        const val TAG = "SoundView"
        const val MSG_UPDATE_DURATION = 1
        const val MSG_UPDATE_PLAY_DURATION = 2


        const val TEXT_GRAVITY_START = 0
        const val TEXT_GRAVITY_END = 1
    }

    data class LoadSoundTask(
        val voice: VoiceData
    )

    private val binding = LayoutSoundViewBinding.inflate(LayoutInflater.from(context), this)

    private var durationTextSize = 12

    private var showPlayButton: Boolean = true
    private var showWave: Boolean = true

    private var voiceData: VoiceData? = null
    private var loadingAnimation: Animation? = null

    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                MSG_UPDATE_DURATION -> {
                    if (voiceData?.duration == null) {
                        binding.tvDuration.text = null
                        return
                    }
                    binding.tvDuration.text = "${voiceData?.duration.getDuration()}\""
                }

                MSG_UPDATE_PLAY_DURATION -> {
                    if (voiceData?.duration == null) {
                        binding.tvDuration.text = null
                        return
                    }
                    val soundDuration = voiceData?.duration.getDuration()
                    val currentPosition =
                        AudioPlayer.getInstance().playPosition.toLong().getDuration()

                    binding.tvDuration.text = "${max(soundDuration - currentPosition, 0)}\""
                    if (AudioPlayer.getInstance().isPlaying && AudioPlayer.getInstance().path == voiceData?.path) {
                        sendEmptyMessageDelayed(MSG_UPDATE_PLAY_DURATION, 1000L)
                    } else {
                        sendEmptyMessage(MSG_UPDATE_DURATION)
                    }
                }
            }
        }
    }

    init {
        binding.root.setBackgroundResource(R.drawable.common_playmate_sound_widget_bg)
        if (isInEditMode) {
            binding.tvDuration.text = "1000\""
        }

        initAttr(context, attrs)
        binding.root.onClick {
            playOrStop()
        }
    }

    private fun initAttr(context: Context, attrs: AttributeSet? = null) {
        var textGravity: Int = TEXT_GRAVITY_START
        context.withStyledAttributes(
            attrs,
            R.styleable.SoundView
        ) {
            durationTextSize = getDimensionPixelSize(
                R.styleable.SoundView_sound_duration_text_size,
                12
            )
            textGravity = getInt(
                R.styleable.SoundView_sound_duration_text_gravity,
                TEXT_GRAVITY_START
            )
            showWave = getBoolean(
                R.styleable.SoundView_sound_show_wave,
                showWave
            )
            showPlayButton = getBoolean(
                R.styleable.SoundView_sound_show_play_button,
                showPlayButton
            )
        }

        binding.tvDuration.setTextSize(TypedValue.COMPLEX_UNIT_PX, durationTextSize.toFloat())
        binding.ivWave.show(showWave)
        binding.btnPlay.show(showPlayButton)

        if (textGravity == TEXT_GRAVITY_START) {
            binding.tvDuration.updateLayoutParams<LayoutParams> {
                width = LayoutParams.WRAP_CONTENT
            }
        } else {
            binding.tvDuration.updateLayoutParams<LayoutParams> {
                width = 0
            }
        }
    }

    fun setSoundUrl(url: String?) {
        this.voiceData = VoiceData(url = url, null)
        stopPlay()
        loadSound()
    }

    fun setSound(voice: VoiceData?) {
        this.voiceData = voice
        stopPlay()
        loadSound()
    }

    private fun loadSound() {
        val voiceData = this.voiceData
        if (voiceData == null || voiceData.isNull()) {
            Log.d(TAG, "loadSound return, for voiceData is null")
            return
        }

        fun onDownloadFinish(downloadPath: String) {
            Log.d(TAG, "onDownloadFinish, path:$downloadPath")
            val loadSoundTask = LoadSoundTask(voiceData)
            MediaPlayer().apply {
                try {
                    setDataSource(downloadPath)

                    // 设置准备完成监听器
                    setOnPreparedListener { mp ->
                        loadSoundTask.voice.duration = mp.duration.toLong()
                        Log.d(
                            TAG,
                            "onDownloadFinish.loadSound, onPrepared, duration:${voiceData.duration}, voice:$voiceData"
                        )
                        handler.sendEmptyMessage(MSG_UPDATE_DURATION)

                        //释放
                        release()
                    }

                    // 设置错误监听器
                    setOnErrorListener { _, what, extra ->
                        Log.e(TAG, "onDownloadFinish.loadSound error")
                        loadSoundTask.voice.duration = null
                        handler.sendEmptyMessage(MSG_UPDATE_DURATION)
                        false
                    }

                    setOnCompletionListener {
                        release()
                    }

                    // 异步准备音频
                    prepareAsync()
                } catch (e: Exception) {
                    Log.d(TAG, "onDownloadFinish.loadSound fail, for ${e.message}", e)
                    release()
                }
            }
        }

        downloadSound(
            voiceData,
            onFinish = { soundPath: String ->
                onDownloadFinish(soundPath)
            }
        )
    }

    private fun downloadSound(
        voiceData: VoiceData?,
        onStartDownload: (() -> Unit)? = null,
        onDownloadProgress: (() -> Unit)? = null,
        onFinish: ((soundPath: String) -> Unit)? = null,
        onError: (() -> Unit)? = null
    ) {
        if (voiceData == null || voiceData.isNull()) {
            onError?.invoke()
            return
        }

        val path = voiceData.path
        if (!path.isNullOrEmpty()) {
            onFinish?.invoke(path)
            return
        }

        val url = voiceData.url
        if (url.isNullOrEmpty()) {
            Log.e(TAG, "downloadSound fail, url is null")
            onError?.invoke()
            return
        }
        val soundPath = url.getFilePathBy(audioPath, UploadFile.FileType.AUDIO)
        if (soundPath.isNullOrEmpty()) {
            Log.e(TAG, "downloadSound fail, can not getFilePath for $url")
            onError?.invoke()
            return
        }
        voiceData.path = soundPath
        val soundFile = storageService.file.createWeFile(soundPath)
        if (soundFile.exists()) {
            Log.d(TAG, "downloadSound return, for sound exists, path:$soundPath")
            onFinish?.invoke(soundPath)
            return
        }
        Log.d(TAG, "downloadSound, url:$url, path:$soundPath")
        val taskId = url.md5()
        val task = Task(taskId, url, soundPath)
        task.listeners.add(object : TaskListener {
            override fun onFinished(task: Task) {
                runOnUiThread {
                    onFinish?.invoke(task.savePath)
                }
            }

            override fun onProgress(
                task: Task,
                progress: Int?,
                currentSize: Long,
                totalSize: Long
            ) {
            }

            override fun onError(task: Task, error: IError) {
                runOnUiThread {
                    onError?.invoke()
                }
            }
        })
        Log.d(
            TAG,
            "downloadSound, start download, url:$url, task:$task"
        )
        App.instance.downloadService.addTask(task)
    }

    fun playOrStop() {
        if (AudioPlayer.getInstance().isPlaying) {
            stopPlay()
            return
        }
        downloadSound(
            voiceData,
            onFinish = { soundPath ->
                if (AudioPlayer.getInstance().isPlaying && AudioPlayer.getInstance().path == soundPath) {
                    stopPlay()
                } else {
                    startPlay(soundPath)
                }
            },
            onStartDownload = {
                playLoadingAnim()
            },
            onDownloadProgress = {
                playLoadingAnim()
            },
            onError = {
                stopLoadingAnim()
            }
        )
    }

    private fun playLoadingAnim() {
        binding.btnPlay.setImageResource(R.drawable.common_playmate_sound_play_ic)
        if (loadingAnimation != null) {
            return
        }
        loadingAnimation?.cancel()
        loadingAnimation = RotateAnimation(
            0f,
            360f,
            Animation.RELATIVE_TO_SELF,
            0.5f,
            Animation.RELATIVE_TO_SELF,
            0.5f
        ).apply {
            duration = 1000
            repeatMode = Animation.RESTART
            repeatCount = Animation.INFINITE
            interpolator = LinearInterpolator()
        }
        binding.btnPlay.animation = loadingAnimation
        loadingAnimation?.start()
    }

    private fun stopLoadingAnim() {
        loadingAnimation?.cancel()
        loadingAnimation = null
    }

    private fun startPlay(soundPath: String) {
        AudioPlayer.getInstance().stopPlay()
        AudioPlayer.getInstance().startPlay(soundPath, object : AudioPlayer.Callback {
            override fun onPlayStart() {
                if (isAttachedToWindow && soundPath == this@SoundView.voiceData?.path) {
                    onStartPlay()
                }
            }

            override fun onCompletion(success: Boolean?) {
                if (isAttachedToWindow && soundPath == this@SoundView.voiceData?.path) {
                    runOnUiThread {
                        onStopPlay()
                    }
                }
            }
        })
    }

    fun stopPlay() {
        AudioPlayer.getInstance().stopPlay()
        onStopPlay()
    }

    private fun onStartPlay() {
        binding.btnPlay.setImageResource(R.drawable.common_playmate_sound_pause_ic)
        binding.ivWave.setActualImageResource(R.drawable.common_sound_wave_anim)
        handler.sendEmptyMessage(MSG_UPDATE_PLAY_DURATION)
    }

    private fun onStopPlay() {
        stopLoadingAnim()
        binding.btnPlay.setImageResource(R.drawable.common_playmate_sound_play_ic)
        binding.ivWave.setActualImageResource(R.drawable.common_sound_wave_ic)
        handler.sendEmptyMessage(MSG_UPDATE_DURATION)
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (isInEditMode) {
            return
        }
        stopPlay()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        if (isInEditMode) {
            return
        }
        stopPlay()
    }


}