package tech.nut.nut_player_android

import android.content.Context
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodChannel
import tech.nut.nut_player_android.models.getMappedContent
import tech.nut.player.NutPlayer
import tech.nut.player.model.Content
import tech.nut.player.model.DashContent
import tech.nut.player.model.ErrorState
import tech.nut.player.model.HlsContent
import tech.nut.player.model.MilliSecond
import tech.nut.player.model.NutPlayer
import tech.nut.player.model.NutPlayerState
import tech.nut.player.model.PlaybackSpeed
import tech.nut.player.model.ProgressiveContent
import tech.nut.player.model.addPlugins

internal class PlayerFlutterPlatform(
    context: Context,
    private val playerContent: String,
    channel: EventChannel,
    private val pluginChannel: MethodChannel,
) {

    var nutPlayer: NutPlayer?
    private var eventSink: EventSink? = null
    private val nutPlayerStateChangeListenerPlugin = NutPlayerStateChangeListenerPlugin()
    private val nutPlayerLoggerPlugin = NutPlayerLoggerPlugin()

    init {
        nutPlayer = null

        channel.setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventSink) {
                    eventSink = events
                    initializePlayer(context)
                }

                override fun onCancel(arguments: Any?) {}
            }
        )
    }

    val position: MilliSecond
        get() = nutPlayer?.position ?: 0

    val duration: MilliSecond
        get() = nutPlayer?.duration ?: 0

    val volume: Float
        get() = nutPlayer?.volume ?: 0F

    fun dispose() = nutPlayer?.release()

    fun play() = nutPlayer?.play()

    fun pause() = nutPlayer?.pause()

    fun seek(position: Long) = nutPlayer?.seekTo(position)

    fun setVolume(volume: Float) = nutPlayer?.setVolume(volume)

    fun setPlaybackSpeed(speed: Double) {
        nutPlayer?.playbackSpeed = when (speed) {
            in 0.0..0.25 -> PlaybackSpeed.x0_25
            in 0.25..0.5 -> PlaybackSpeed.x0_5
            in 0.5..0.75 -> PlaybackSpeed.x0_75
            in 0.75..1.0 -> PlaybackSpeed.x1
            in 1.0..1.25 -> PlaybackSpeed.x1_25
            in 1.25..1.5 -> PlaybackSpeed.x1_5
            in 1.5..1.75 -> PlaybackSpeed.x1_75
            in 1.75..2.0 -> PlaybackSpeed.x2
            else -> PlaybackSpeed.x1
        }
    }

    // MARK: - Private

    private fun initializePlayer(context: Context) {
        val player = NutPlayer(context)
        nutPlayer = player
        nutPlayer?.addPlugins(
            listOf(
                nutPlayerLoggerPlugin.apply {
                    onMessageFetched = ::addLogMessage
                },
                nutPlayerStateChangeListenerPlugin.apply {
                    onMessageFetched = ::sendMessage
                    onMessageFetchedError = ::sendErrorMessage
                }
            )
        )
        val inputContent = createPlayerContent(playerContent)
        inputContent?.let {
            val map = mapOf<String, Any>(
                "event" to "initialized"
            )
            sendMessage(map)
            nutPlayer?.load(inputContent = inputContent)
        }
    }

    private fun sendMessage(map: Map<String, Any>) {
        val parsedMap = map.toMutableMap()
        if (parsedMap.containsKey("duration")) {
            parsedMap["duration"] = duration
        }
        eventSink?.success(parsedMap)
    }

    private fun sendErrorMessage(state: NutPlayerState) {
        /*eventSink?.error(
            "NutPlayerError",
            (state as ErrorState).errors.last().localizedMessage,
            (state).errors.last()
        )*/
    }

    private fun addLogMessage(message: String) {
        pluginChannel.invokeMethod("pluginLogMessage", mapOf("message" to message))
    }

    private fun createPlayerContent(rawPlayerContent: String): Content? {
        val mappedContent = rawPlayerContent.getMappedContent()
        return when (mappedContent.content.type) {
            "dash" -> DashContent(
                url = mappedContent.content.url,
                stats = mappedContent.stats,
                captions = mappedContent.captions
            )

            "hls" -> HlsContent(
                url = mappedContent.content.url,
                stats = mappedContent.stats,
                captions = mappedContent.captions
            )

            "mp4" -> ProgressiveContent(
                url = mappedContent.content.url,
                stats = mappedContent.stats,
                captions = mappedContent.captions
            )

            else -> null
        }
    }
}