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.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.gone
import com.adealink.weparty.commonui.ext.show
import com.adealink.weparty.databinding.LayoutSoundViewBinding
import com.adealink.weparty.storage.file.FilePath.audioPath
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 url: String
    )

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

    private var durationTextSize = 12f
    private var showWave: Boolean = false

    private var soundUrl: String? = null
    private var soundPath: String? = null
    private var soundDuration: Long? = 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 -> {
                    binding.tvDuration.text = "${max(((soundDuration ?: 0) / 1000).toInt(), 1)}\""
                }

                MSG_UPDATE_PLAY_DURATION -> {
                    binding.tvDuration.text =
                        "${max(((AudioPlayer.getInstance().playPosition ?: 0) / 1000), 1)}\""
                    if (AudioPlayer.getInstance().isPlaying && AudioPlayer.getInstance().path == soundPath) {
                        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)
        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 = getFloat(
                R.styleable.SoundView_sound_duration_text_size,
                12f
            )
            textGravity = getInt(
                R.styleable.SoundView_sound_duration_text_gravity,
                TEXT_GRAVITY_START
            )
            showWave = getBoolean(
                R.styleable.SoundView_sound_show_wave,
                false
            )
        }

        binding.tvDuration.textSize = durationTextSize
        if (showWave) {
            binding.ivWave.show()
        } else {
            binding.ivWave.gone()
        }

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

    fun setSoundUrl(url: String?) {
        this.soundUrl = url
        this.soundPath = null
        this.soundDuration = null
        stopPlay()
        loadSound()
    }

    private fun loadSound() {
        val url = soundUrl
        if (url.isNullOrEmpty()) {
            Log.e(TAG, "playSound fail, url is null")
            return
        }

        downloadSound(
            onFinish = { soundPath ->
                Log.d(TAG, "loadSound, path:$soundPath")
                val loadSoundTask = LoadSoundTask(url)
                MediaPlayer().apply {
                    try {
                        setDataSource(soundPath)

                        // 设置准备完成监听器
                        setOnPreparedListener { mp ->
                            Log.e(TAG, "loadSound, onPrepared")
                            if (loadSoundTask.url == soundUrl) {
                                soundDuration = mp.duration.toLong()
                                Log.e(TAG, "loadSound, url:$soundUrl, duration:$soundDuration")
                                handler.sendEmptyMessage(MSG_UPDATE_DURATION)
                            }

                            //释放
                            release()
                        }

                        // 设置错误监听器
                        setOnErrorListener { _, what, extra ->
                            Log.e(TAG, "loadSound error")
                            if (loadSoundTask.url == soundUrl) {
                                soundDuration = 0
                                handler.sendEmptyMessage(MSG_UPDATE_DURATION)
                            }
                            false
                        }

                        setOnCompletionListener {
                            release()
                        }

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

    private fun downloadSound(
        onStartDownload: (() -> Unit)? = null,
        onDownloadProgress: (() -> Unit)? = null,
        onFinish: ((soundPath: String) -> Unit)? = null,
        onError: (() -> Unit)? = null
    ) {
        val url = soundUrl
        if (url.isNullOrEmpty()) {
            Log.e(TAG, "downloadSound fail, url is null")
            return
        }
        val soundPath = url.getFilePathBy(audioPath, UploadFile.FileType.MP3)
        if (soundPath.isNullOrEmpty()) {
            Log.e(TAG, "downloadSound fail, can not getFilePath for $url")
            return
        }
        this.soundPath = 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) {
                if (isAttachedToWindow && task.path == url) {
                    runOnUiThread {
                        onStartDownload?.invoke()
                    }
                }
            }

            override fun onProgress(
                task: Task,
                progress: Int?,
                currentSize: Long,
                totalSize: Long
            ) {
                if (isAttachedToWindow && task.path == url) {
                    runOnUiThread {
                        onDownloadProgress?.invoke()
                    }
                }
            }

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

    fun playOrStop() {
        downloadSound(
            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 onCompletion(success: Boolean?) {
                if (isAttachedToWindow && soundPath == this@SoundView.soundPath) {
                    runOnUiThread {
                        onStopPlay()
                    }
                }
            }
        })
        onStartPlay()
    }

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

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

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

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        stopPlay()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        stopPlay()
    }


}