package kr.co.vividnext.sodalive.explorer.profile.creator_community.write

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.FragmentRecordingVoiceBinding
import java.io.File
import java.io.IOException
import java.util.Locale
import kotlin.math.roundToInt

class RecordingVoiceFragment : BottomSheetDialogFragment() {

    private var listener: OnAudioRecordedListener? = null
    private var mediaRecorder: MediaRecorder? = null
    private var mediaPlayer: MediaPlayer? = null
    private var fileNameMedia = ""

    private lateinit var binding: FragmentRecordingVoiceBinding

    private var startTime: Long = 0L
    private val handler = Handler(Looper.getMainLooper())

    interface OnAudioRecordedListener {
        fun onAudioRecorded(file: File)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is OnAudioRecordedListener) {
            listener = context
        } else {
            throw RuntimeException("$context must implement OnAudioRecordedListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState)

        dialog.setOnShowListener {
            val d = it as BottomSheetDialog
            val bottomSheet = d.findViewById<FrameLayout>(
                com.google.android.material.R.id.design_bottom_sheet
            )
            if (bottomSheet != null) {
                val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
                bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
                bottomSheetBehavior.isDraggable = false
            }
        }

        return dialog
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentRecordingVoiceBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.ivClose.setOnClickListener {
            deleteAudioFile()
            dismiss()
        }
        binding.ivRecordStart.setOnClickListener {
            fileNameMedia = requireActivity().filesDir.path +
                "/now_voice_${System.currentTimeMillis()}.m4a"

            val fileMedia = File(fileNameMedia)
            if (!fileMedia.exists()) {
                try {
                    fileMedia.createNewFile()
                    startRecording()
                } catch (e: IOException) {
                    Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
                    e.printStackTrace()
                }
            }
        }

        binding.ivRecordStop.setOnClickListener { stopRecording() }
        binding.ivRecordPlay.setOnClickListener { startPlaying() }
        binding.ivRecordPause.setOnClickListener { stopPlaying() }

        binding.tvDelete.setOnClickListener {
            deleteAudioFile()

            binding.ivRecordStart.visibility = View.VISIBLE
            binding.llRetryOrComplete.visibility = View.GONE
            binding.rlRecordPlay.visibility = View.GONE
            binding.soundVisualizer.visibility = View.GONE
        }

        binding.tvRetryRecord.setOnClickListener {
            deleteAudioFile()

            binding.ivRecordStart.visibility = View.VISIBLE
            binding.llRetryOrComplete.visibility = View.GONE
            binding.rlRecordPlay.visibility = View.GONE
            binding.soundVisualizer.visibility = View.GONE
        }

        binding.tvComplete.setOnClickListener {
            listener?.onAudioRecorded(file = File(fileNameMedia))
            dismiss()
        }
    }

    override fun onDetach() {
        super.onDetach()
        listener = null
    }

    override fun onDestroy() {
        releaseMediaPlayer()
        releaseMediaRecorder()

        super.onDestroy()
    }

    private fun deleteAudioFile() {
        if (fileNameMedia.isNotBlank()) {
            val fileMedia = File(fileNameMedia)
            if (fileMedia.exists()) {
                fileMedia.delete()
            }
            fileNameMedia = ""
        }
    }

    private fun startRecording() {
        releaseMediaRecorder()

        // safety check, don't start a new recording if one is already going
        mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            MediaRecorder(requireContext())
        } else {
            MediaRecorder()
        }.apply {
            setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
            setAudioChannels(2)
            setAudioSamplingRate(48000)
            setAudioEncodingBitRate(256000)
            setOutputFile(fileNameMedia)
        }

        try {
            mediaRecorder!!.prepare()
        } catch (e: Exception) {
            Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
            return
        }

        mediaRecorder!!.start()
        binding.ivRecordStart.visibility = View.GONE
        binding.ivRecordStop.visibility = View.VISIBLE

        startCountDownTimer()
    }

    private fun releaseMediaRecorder() {
        if (mediaRecorder != null) {
            // stop recording and free up resources
            mediaRecorder!!.stop()
            mediaRecorder!!.reset()
            mediaRecorder!!.release()

            mediaRecorder = null
        }
    }

    private fun stopRecording() {
        releaseMediaRecorder()
        stopCountDownTimer()

        binding.ivRecordStop.visibility = View.GONE
        binding.rlRecordPlay.visibility = View.VISIBLE
        binding.llRetryOrComplete.visibility = View.VISIBLE
    }

    private fun startPlaying() {
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer()
            mediaPlayer!!.reset()

            mediaPlayer!!.setOnCompletionListener {
                releaseMediaPlayer()
                stopCountDownTimer()

                binding.tvDelete.visibility = View.VISIBLE
                binding.ivRecordPlay.visibility = View.VISIBLE
                binding.llRetryOrComplete.visibility = View.VISIBLE
                binding.ivRecordPause.visibility = View.GONE
                binding.soundVisualizer.visibility = View.GONE
            }

            mediaPlayer!!.setOnPreparedListener {
                binding.soundVisualizer.visibility = View.VISIBLE
                binding.soundVisualizer.setAudioSessionId(mediaPlayer!!.audioSessionId)
                it.start()

                startCountDownTimer()
            }

            try {
                mediaPlayer!!.setDataSource(fileNameMedia)
                mediaPlayer!!.prepare()
            } catch (e: Exception) {
                Toast.makeText(requireActivity(), R.string.retry, Toast.LENGTH_LONG).show()
                return
            }

            binding.tvDelete.visibility = View.GONE
            binding.ivRecordPlay.visibility = View.GONE
            binding.llRetryOrComplete.visibility = View.GONE
            binding.ivRecordPause.visibility = View.VISIBLE
        }
    }

    private fun stopPlaying() {
        releaseMediaPlayer()
        stopCountDownTimer()

        binding.tvDelete.visibility = View.VISIBLE
        binding.ivRecordPlay.visibility = View.VISIBLE
        binding.llRetryOrComplete.visibility = View.VISIBLE
        binding.ivRecordPause.visibility = View.GONE
        binding.soundVisualizer.visibility = View.GONE
    }

    private fun releaseMediaPlayer() {
        if (mediaPlayer != null) {
            mediaPlayer!!.release()
            mediaPlayer = null
        }
    }

    private fun startCountDownTimer() {
        startTime = System.currentTimeMillis()

        handler.post(object : Runnable {
            override fun run() {
                // 현재 시간에서 시작 시간을 빼서 경과 시간 계산
                val elapsedMilliseconds = System.currentTimeMillis() - startTime
                val centisecondsElapsed = (elapsedMilliseconds / 10.0).roundToInt()

                // 경과한 시간에서 분, 초, 센티초 계산
                val minutes = centisecondsElapsed / 6000
                val seconds = (centisecondsElapsed % 6000) / 100
                val centiseconds = centisecondsElapsed % 100

                val time = String.format(
                    Locale.getDefault(),
                    "%02d:%02d.%02d",
                    minutes,
                    seconds,
                    centiseconds
                )
                binding.tvTimer.text = time

                if (minutes >= 3) {
                    stopRecording()
                }

                // 10밀리초(1센티초)마다 실행
                handler.postDelayed(this, 10)
            }
        })
    }

    @SuppressLint("SetTextI18n")
    private fun stopCountDownTimer() {
        handler.removeCallbacksAndMessages(null)
        startTime = 0
        binding.tvTimer.text = "00:00.00"
    }
}
