package kr.co.vividnext.sodalive.audio_content

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.core.app.NotificationCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.main.MainActivity
import org.koin.android.ext.android.inject

class AudioContentPlayService :
    Service(),
    MediaPlayer.OnPreparedListener,
    MediaPlayer.OnCompletionListener {

    private var playbackTrackingId: Long = 0
    private val playbackTrackingRepository: PlaybackTrackingRepository by inject()

    private lateinit var mediaPlayer: MediaPlayer
    private var url: String? = null
    private var title: String? = null
    private var isFree: Boolean? = null
    private var isPreview: Boolean? = null
    private var nickname: String? = null
    private var contentId: Long? = null
    private var creatorId: Long? = null
    private var coverImageUrl: String? = null

    private var isPlaying = false

    private val handler = Handler(Looper.getMainLooper())
    private var changeMediaPlayerPositionRunnable = object : Runnable {
        override fun run() {
            val intent = Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER).apply {
                putExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, mediaPlayer.currentPosition)
                putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
            }
            intent.setPackage(packageName)
            sendBroadcast(intent)
            handler.postDelayed(this, 1000)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            MusicAction.INIT.name -> {
                val contentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
                if (this.contentId != null && this.contentId == contentId) {
                    sendBroadcast(
                        Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                            .apply {
                                setPackage(packageName)
                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI,
                                    true
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_PLAYING,
                                    isPlaying
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_DURATION,
                                    mediaPlayer.duration
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_PROGRESS,
                                    mediaPlayer.currentPosition
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_ID,
                                    contentId
                                )
                            }
                    )
                }

                if (
                    this.contentId != null &&
                    title != null &&
                    nickname != null &&
                    coverImageUrl != null
                ) {
                    sendBroadcast(
                        Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
                            .apply {
                                setPackage(packageName)
                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_SHOWING,
                                    true
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_PLAYING,
                                    isPlaying
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_TITLE,
                                    title
                                )

                                putExtra(
                                    Constants.EXTRA_NICKNAME,
                                    nickname
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL,
                                    coverImageUrl
                                )

                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_ID,
                                    this@AudioContentPlayService.contentId
                                )
                            }
                    )
                } else {
                    sendBroadcast(
                        Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
                            .apply {
                                setPackage(packageName)
                                putExtra(
                                    Constants.EXTRA_AUDIO_CONTENT_SHOWING,
                                    false
                                )
                            }
                    )
                }
            }

            MusicAction.PLAY.name -> {
                if (!isPlaying && this::mediaPlayer.isInitialized) {
                    mediaPlayer.start()
                    toggleIsPlaying()
                    updateNotification()
                }
            }

            MusicAction.PAUSE.name -> {
                if (isPlaying && this::mediaPlayer.isInitialized) {
                    mediaPlayer.pause()
                    toggleIsPlaying()
                    updateNotification()
                }
            }

            MusicAction.STOP.name -> {
                if (this::mediaPlayer.isInitialized) {
                    mediaPlayer.stop()
                    setEndPositionPlaybackTracking(mediaPlayer.currentPosition)
                    toggleIsPlaying(false)
                    onStopService()
                }
            }

            MusicAction.CONDITIONAL_STOP.name -> {
                val contentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
                if (
                    this.contentId != null &&
                    this.contentId == contentId &&
                    this::mediaPlayer.isInitialized
                ) {
                    mediaPlayer.stop()
                    setEndPositionPlaybackTracking(mediaPlayer.currentPosition)
                    toggleIsPlaying(false)
                    onStopService()
                }
            }

            MusicAction.PROGRESS.name -> {
                val progress = intent.getIntExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, 0)
                if (progress > 0 && this::mediaPlayer.isInitialized) {
                    if (contentId != null) {
                        saveNewPlaybackTracking(
                            totalDuration = mediaPlayer.duration,
                            progress = progress
                        )
                    }

                    mediaPlayer.seekTo(progress)
                }
            }

            else -> {
                val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
                if (contentId != null && this.contentId == contentId) {
                    if (isPlaying) {
                        sendBroadcast(
                            Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                                .apply {
                                    setPackage(packageName)
                                    putExtra(
                                        Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
                                        MusicAction.PAUSE
                                    )

                                    putExtra(
                                        Constants.EXTRA_AUDIO_CONTENT_ID,
                                        contentId
                                    )
                                }
                        )
                    } else {
                        sendBroadcast(
                            Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                                .apply {
                                    setPackage(packageName)
                                    putExtra(
                                        Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
                                        MusicAction.PLAY
                                    )

                                    putExtra(
                                        Constants.EXTRA_AUDIO_CONTENT_ID,
                                        contentId
                                    )
                                }
                        )
                    }
                } else {
                    url = intent?.getStringExtra(Constants.EXTRA_AUDIO_CONTENT_URL)
                    title = intent?.getStringExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE)
                    isFree = intent?.getBooleanExtra(
                        Constants.EXTRA_AUDIO_CONTENT_FREE,
                        true
                    )
                    isPreview = intent?.getBooleanExtra(
                        Constants.EXTRA_AUDIO_CONTENT_PREVIEW,
                        true
                    )
                    nickname = intent?.getStringExtra(Constants.EXTRA_NICKNAME)
                    coverImageUrl = intent?.getStringExtra(
                        Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL
                    )
                    creatorId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID, 0)
                    this.contentId = contentId

                    if (url != null) {
                        sendBroadcast(
                            Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                                .apply {
                                    setPackage(packageName)
                                    putExtra(
                                        Constants.EXTRA_AUDIO_CONTENT_LOADING,
                                        true
                                    )
                                }
                        )

                        if (isPlaying && this::mediaPlayer.isInitialized) {
                            mediaPlayer.stop()
                            setEndPositionPlaybackTracking(mediaPlayer.currentPosition)

                            mediaPlayer.release()
                            toggleIsPlaying()
                        }

                        initMediaPlayer()
                        mediaPlayer.setDataSource(url)
                        mediaPlayer.prepareAsync()
                    }
                }
            }
        }

        return START_NOT_STICKY
    }

    override fun onDestroy() {
        if (this::mediaPlayer.isInitialized) {
            mediaPlayer.release()
        }

        onStopService()
        super.onDestroy()
    }

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onCompletion(mp: MediaPlayer) {
        setEndPositionPlaybackTracking(mp.currentPosition)

        if (SharedPreferenceManager.isContentPlayLoop) {
            saveNewPlaybackTracking(totalDuration = mp.duration, progress = 0)
            mp.start()
        } else {
            toggleIsPlaying(false)
            mediaPlayer.release()
            onStopService()
        }
    }

    private fun toggleIsPlaying(isPlaying: Boolean? = null) {
        this.isPlaying = isPlaying ?: !this.isPlaying
        if (this.isPlaying) {
            handler.postDelayed(changeMediaPlayerPositionRunnable, 1000)
        } else {
            handler.removeCallbacks(changeMediaPlayerPositionRunnable)
        }

        sendBroadcast(
            Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                .apply {
                    setPackage(packageName)
                    putExtra(
                        Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI,
                        true
                    )

                    putExtra(
                        Constants.EXTRA_AUDIO_CONTENT_PLAYING,
                        this@AudioContentPlayService.isPlaying
                    )

                    putExtra(
                        Constants.EXTRA_AUDIO_CONTENT_ID,
                        contentId
                    )
                }
        )

        if (isPlaying != null && !isPlaying) {
            resetAudioData()
        }

        sendBroadcast(
            Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
                .apply {
                    setPackage(packageName)
                    putExtra(
                        Constants.EXTRA_AUDIO_CONTENT_PLAYING,
                        this@AudioContentPlayService.isPlaying
                    )

                    putExtra(
                        Constants.EXTRA_AUDIO_CONTENT_SHOWING,
                        contentId != null
                    )
                }
        )
    }

    private fun resetAudioData() {
        url = null
        title = null
        nickname = null
        contentId = null
    }

    private fun initMediaPlayer() {
        mediaPlayer = MediaPlayer()
        mediaPlayer.setOnPreparedListener(this)
        mediaPlayer.setOnCompletionListener(this)
        mediaPlayer.setAudioAttributes(
            AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()
        )
    }

    override fun onPrepared(mp: MediaPlayer?) {
        if (this::mediaPlayer.isInitialized) {
            saveNewPlaybackTracking(totalDuration = mediaPlayer.duration, progress = 0)
            sendBroadcast(
                Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
                    .apply {
                        setPackage(packageName)
                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
                            MusicAction.PLAY
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_DURATION,
                            mediaPlayer.duration
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_ID,
                            contentId
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_ALERT_PREVIEW,
                            true
                        )
                    }
            )

            sendBroadcast(
                Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
                    .apply {
                        setPackage(packageName)
                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_PLAYING,
                            false
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_SHOWING,
                            true
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_TITLE,
                            title
                        )

                        putExtra(
                            Constants.EXTRA_NICKNAME,
                            nickname
                        )

                        putExtra(
                            Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL,
                            coverImageUrl
                        )
                    }
            )
        }
    }

    private fun updateNotification() {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this,
            0,
            intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
        )

        val channelId = "audio_content_play_channel"
        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "콘텐츠 알림 채널",
                NotificationManager.IMPORTANCE_LOW
            )
            notificationManager.createNotificationChannel(channel)
        }

        val playPauseIcon =
            if (isPlaying) R.drawable.ic_noti_pause else R.drawable.ic_noti_play
        val playPauseAction =
            if (isPlaying) MusicAction.PAUSE.name else MusicAction.PLAY.name

        Glide
            .with(this)
            .asBitmap()
            .load(coverImageUrl)
            .into(object : CustomTarget<Bitmap>() {
                override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                    val notificationBuilder = NotificationCompat
                        .Builder(this@AudioContentPlayService, channelId)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(resource)
                        .setContentTitle(title ?: "오디오 콘텐츠")
                        .setContentText(nickname ?: "")
                        .setContentIntent(pendingIntent)
                        .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                        .addAction(
                            NotificationCompat
                                .Action
                                .Builder(
                                    playPauseIcon,
                                    "Play_or_Pause",
                                    getServiceIntent(playPauseAction)
                                ).build()
                        )
                        .addAction(
                            NotificationCompat
                                .Action
                                .Builder(
                                    R.drawable.ic_noti_stop,
                                    "Stop",
                                    getServiceIntent(MusicAction.STOP.name)
                                ).build()
                        )

                    notificationBuilder.setStyle(
                        androidx.media.app.NotificationCompat.MediaStyle()
                            .setShowActionsInCompactView(0, 1)
                    )

                    val notification = notificationBuilder.build()
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        startForeground(
                            1,
                            notification,
                            FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
                        )
                    } else {
                        startForeground(1, notification)
                    }
                }

                override fun onLoadCleared(placeholder: Drawable?) {
                }
            })
    }

    private fun getServiceIntent(action: String): PendingIntent {
        val intent = Intent(this, AudioContentPlayService::class.java)
        intent.action = action
        return PendingIntent.getService(
            this,
            0,
            intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
        )
    }

    private fun saveNewPlaybackTracking(totalDuration: Int, progress: Int) {
        if (creatorId != SharedPreferenceManager.userId) {
            playbackTrackingId = playbackTrackingRepository.savePlaybackTracking(
                PlaybackTracking(
                    contentId = contentId!!,
                    totalDuration = totalDuration,
                    startPosition = progress,
                    isFree = isFree!!,
                    isPreview = isPreview!!
                )
            )
        }
    }

    private fun setEndPositionPlaybackTracking(progress: Int) {
        if (creatorId != SharedPreferenceManager.userId && playbackTrackingId > 0) {
            val playbackTracking = playbackTrackingRepository
                .getPlaybackTracking(playbackTrackingId)

            if (playbackTracking != null) {
                playbackTracking.endPosition = progress
                playbackTrackingRepository.savePlaybackTracking(playbackTracking)
            }

            playbackTrackingId = 0
        }
    }

    private fun onStopService() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            stopForeground(STOP_FOREGROUND_REMOVE)
        } else {
            stopForeground(true)
        }

        stopSelf()
    }

    enum class MusicAction {
        PLAY, PAUSE, STOP, PROGRESS, INIT, CONDITIONAL_STOP
    }
}
