package com.smplayer.smplayer

import android.app.Activity
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Rational
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

class PipBridge(
    private val activity: Activity,
    flutterEngine: FlutterEngine,
) {
    companion object {
        private const val METHOD_CHANNEL = "smplayer/pip"
        private const val EVENT_CHANNEL = "smplayer/pip_events"

        private const val ACTION_PLAY = "com.smplayer.pip.PLAY"
        private const val ACTION_PAUSE = "com.smplayer.pip.PAUSE"
        private const val ACTION_NEXT = "com.smplayer.pip.NEXT"
        private const val ACTION_PREV = "com.smplayer.pip.PREV"
        private const val ACTION_SEEK_FWD = "com.smplayer.pip.SEEK_FWD"
        private const val ACTION_SEEK_BWD = "com.smplayer.pip.SEEK_BWD"

        private const val REQUEST_PLAY = 1
        private const val REQUEST_PAUSE = 2
        private const val REQUEST_NEXT = 3
        private const val REQUEST_PREV = 4
        private const val REQUEST_SEEK_FWD = 5
        private const val REQUEST_SEEK_BWD = 6
    }

    private val methodChannel = MethodChannel(
        flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL
    )
    private val eventChannel = EventChannel(
        flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL
    )

    private var eventSink: EventChannel.EventSink? = null
    private val mainHandler = Handler(Looper.getMainLooper())

    private var autoEnterOnLeave = false
    private var aspectX = 16
    private var aspectY = 9
    private var isPlaying = false
    private var hasNext = false
    private var hasPrev = false
    private var useEpisodeActions = true

    private val actionReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action ?: return
            val dartAction = when (action) {
                ACTION_PLAY -> "play"
                ACTION_PAUSE -> "pause"
                ACTION_NEXT -> "next"
                ACTION_PREV -> "prev"
                ACTION_SEEK_FWD -> "seekForward"
                ACTION_SEEK_BWD -> "seekBackward"
                else -> return
            }
            mainHandler.post {
                eventSink?.success(mapOf("type" to "action", "action" to dartAction))
            }
        }
    }

    private var receiverRegistered = false

    init {
        methodChannel.setMethodCallHandler { call, result ->
            when (call.method) {
                "isSupported" -> {
                    result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                }

                "isInPipMode" -> {
                    result.success(
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                            activity.isInPictureInPictureMode
                        else false
                    )
                }

                "configure" -> {
                    val args = call.arguments as? Map<*, *>
                    if (args != null) {
                        aspectX = (args["aspectX"] as? Number)?.toInt() ?: 16
                        aspectY = (args["aspectY"] as? Number)?.toInt() ?: 9
                        autoEnterOnLeave = args["autoEnterOnLeave"] as? Boolean ?: false
                        isPlaying = args["isPlaying"] as? Boolean ?: false
                        hasNext = args["hasNext"] as? Boolean ?: false
                        hasPrev = args["hasPrev"] as? Boolean ?: false
                        useEpisodeActions = args["useEpisodeActions"] as? Boolean ?: true
                        updatePipParams()
                    }
                    result.success(true)
                }

                "enterPip" -> {
                    val args = call.arguments as? Map<*, *>
                    if (args != null) {
                        aspectX = (args["aspectX"] as? Number)?.toInt() ?: aspectX
                        aspectY = (args["aspectY"] as? Number)?.toInt() ?: aspectY
                    }
                    result.success(enterPip())
                }

                "updatePlaybackState" -> {
                    val args = call.arguments as? Map<*, *>
                    if (args != null) {
                        isPlaying = args["isPlaying"] as? Boolean ?: isPlaying
                        hasNext = args["hasNext"] as? Boolean ?: hasNext
                        hasPrev = args["hasPrev"] as? Boolean ?: hasPrev
                        useEpisodeActions = args["useEpisodeActions"] as? Boolean ?: useEpisodeActions
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
                            activity.isInPictureInPictureMode
                        ) {
                            updatePipParams()
                        }
                    }
                    result.success(true)
                }

                else -> result.notImplemented()
            }
        }

        eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
            override fun onListen(arguments: Any?, sink: EventChannel.EventSink?) {
                eventSink = sink
                registerActionReceiver()
            }

            override fun onCancel(arguments: Any?) {
                eventSink = null
                unregisterActionReceiver()
            }
        })
    }

    private fun registerActionReceiver() {
        if (receiverRegistered) return
        val filter = IntentFilter().apply {
            addAction(ACTION_PLAY)
            addAction(ACTION_PAUSE)
            addAction(ACTION_NEXT)
            addAction(ACTION_PREV)
            addAction(ACTION_SEEK_FWD)
            addAction(ACTION_SEEK_BWD)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            activity.registerReceiver(actionReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
        } else {
            activity.registerReceiver(actionReceiver, filter)
        }
        receiverRegistered = true
    }

    private fun unregisterActionReceiver() {
        if (!receiverRegistered) return
        try {
            activity.unregisterReceiver(actionReceiver)
        } catch (_: IllegalArgumentException) {}
        receiverRegistered = false
    }

    private fun buildRemoteActions(): List<RemoteAction> {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return emptyList()

        val actions = mutableListOf<RemoteAction>()

        if (useEpisodeActions && (hasNext || hasPrev)) {

            actions.add(
                makeAction(
                    android.R.drawable.ic_media_previous,
                    "上一集",
                    ACTION_PREV,
                    REQUEST_PREV,
                    enabled = hasPrev,
                )
            )
            actions.add(
                if (isPlaying) makeAction(
                    android.R.drawable.ic_media_pause,
                    "暂停",
                    ACTION_PAUSE,
                    REQUEST_PAUSE,
                ) else makeAction(
                    android.R.drawable.ic_media_play,
                    "播放",
                    ACTION_PLAY,
                    REQUEST_PLAY,
                )
            )
            actions.add(
                makeAction(
                    android.R.drawable.ic_media_next,
                    "下一集",
                    ACTION_NEXT,
                    REQUEST_NEXT,
                    enabled = hasNext,
                )
            )
        } else {

            actions.add(
                makeAction(
                    android.R.drawable.ic_media_rew,
                    "快退",
                    ACTION_SEEK_BWD,
                    REQUEST_SEEK_BWD,
                )
            )
            actions.add(
                if (isPlaying) makeAction(
                    android.R.drawable.ic_media_pause,
                    "暂停",
                    ACTION_PAUSE,
                    REQUEST_PAUSE,
                ) else makeAction(
                    android.R.drawable.ic_media_play,
                    "播放",
                    ACTION_PLAY,
                    REQUEST_PLAY,
                )
            )
            actions.add(
                makeAction(
                    android.R.drawable.ic_media_ff,
                    "快进",
                    ACTION_SEEK_FWD,
                    REQUEST_SEEK_FWD,
                )
            )
        }

        return actions
    }

    private fun makeAction(
        iconRes: Int,
        title: String,
        action: String,
        requestCode: Int,
        enabled: Boolean = true,
    ): RemoteAction {
        val intent = Intent(action).setPackage(activity.packageName)
        val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        val pendingIntent = PendingIntent.getBroadcast(activity, requestCode, intent, flags)
        val icon = Icon.createWithResource(activity, iconRes)
        return RemoteAction(icon, title, title, pendingIntent).apply {
            isEnabled = enabled
        }
    }

    private fun buildPipParams(): PictureInPictureParams? {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null

        val builder = PictureInPictureParams.Builder()
            .setAspectRatio(Rational(aspectX, aspectY))
            .setActions(buildRemoteActions())

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            builder.setAutoEnterEnabled(autoEnterOnLeave && isPlaying)
            builder.setSeamlessResizeEnabled(true)
        }

        return builder.build()
    }

    private fun updatePipParams() {
        val params = buildPipParams() ?: return
        try {
            activity.setPictureInPictureParams(params)
        } catch (e: Exception) {

        }
    }

    fun enterPip(): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
        val params = buildPipParams() ?: return false
        return try {
            activity.enterPictureInPictureMode(params)
            true
        } catch (e: Exception) {
            false
        }
    }


    fun onUserLeaveHint() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) return 
        if (!autoEnterOnLeave || !isPlaying) return
        enterPip()
    }


    fun onPipModeChanged(isInPip: Boolean) {
        mainHandler.post {
            eventSink?.success(mapOf("type" to "modeChanged", "isInPip" to isInPip))
        }
    }

    fun dispose() {
        unregisterActionReceiver()
        methodChannel.setMethodCallHandler(null)
        eventChannel.setStreamHandler(null)
        eventSink = null
    }
}
