package com.smplayer.media3_engine

import android.content.Context
import android.os.Handler
import android.os.Looper
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformViewRegistry


class Media3EnginePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
    EventChannel.StreamHandler {

    private lateinit var methodChannel: MethodChannel
    private lateinit var eventChannel: EventChannel
    private lateinit var applicationContext: Context
    private lateinit var binaryMessenger: BinaryMessenger
    private lateinit var platformViewRegistry: PlatformViewRegistry
    private val mainHandler = Handler(Looper.getMainLooper())
    private val players = mutableMapOf<Int, Media3PlayerInstance>()
    private var nextPlayerId = 1
    private var eventSink: EventChannel.EventSink? = null

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        applicationContext = binding.applicationContext
        binaryMessenger = binding.binaryMessenger
        platformViewRegistry = binding.platformViewRegistry

        methodChannel = MethodChannel(binaryMessenger, CHANNEL_METHOD)
        methodChannel.setMethodCallHandler(this)

        eventChannel = EventChannel(binaryMessenger, CHANNEL_EVENTS)
        eventChannel.setStreamHandler(this)


        platformViewRegistry.registerViewFactory(
            VIEW_TYPE,
            Media3PlatformViewFactory { id -> players[id] },
        )
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {

        for (player in players.values) {
            try {
                player.dispose()
            } catch (t: Throwable) {
                android.util.Log.w(TAG, "dispose during detach failed", t)
            }
        }
        players.clear()
        methodChannel.setMethodCallHandler(null)
        eventChannel.setStreamHandler(null)
        eventSink = null
    }


    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        eventSink = events


        for (player in players.values) {
            try {
                player.replayKeyState()
            } catch (t: Throwable) {
                android.util.Log.w(TAG, "replayKeyState failed", t)
            }
        }
    }

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


    internal fun emit(event: Map<String, Any?>) {
        val sink = eventSink ?: return
        if (Looper.myLooper() == Looper.getMainLooper()) {
            sink.success(event)
        } else {
            mainHandler.post { eventSink?.success(event) }
        }
    }


    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        try {
            when (call.method) {
                "create" -> handleCreate(result)
                else -> dispatchToPlayer(call, result)
            }
        } catch (t: Throwable) {
            android.util.Log.e(TAG, "method ${call.method} failed", t)
            result.error("media3_error", t.message, null)
        }
    }

    private fun handleCreate(result: MethodChannel.Result) {
        val id = nextPlayerId++
        val instance = Media3PlayerInstance(
            id = id,
            context = applicationContext,
            mainHandler = mainHandler,
            emit = ::emit,
        )
        players[id] = instance
        val info = instance.create()
        result.success(info)
    }

    private fun dispatchToPlayer(call: MethodCall, result: MethodChannel.Result) {
        val playerId = (call.argument<Number>("playerId"))?.toInt()
            ?: run {
                result.error("missing_player_id", "method ${call.method}", null)
                return
            }
        val player = players[playerId]
            ?: run {

                result.success(null)
                return
            }
        when (call.method) {
            "open" -> player.open(call, result)
            "play" -> { player.play(); result.success(null) }
            "pause" -> { player.pause(); result.success(null) }
            "stop" -> {
                val gen = (call.argument<Number>("generation"))?.toInt() ?: -1
                player.stop(gen)
                result.success(null)
            }
            "seek" -> {
                val ms = (call.argument<Number>("positionMs"))?.toLong() ?: 0L
                player.seek(ms)
                result.success(null)
            }
            "setRate" -> {
                val rate = (call.argument<Number>("rate"))?.toFloat() ?: 1f
                player.setRate(rate)
                result.success(null)
            }
            "setVolume" -> {
                val vol = (call.argument<Number>("volume"))?.toFloat() ?: 100f
                player.setVolume(vol)
                result.success(null)
            }
            "setResizeMode" -> {
                val mode = call.argument<String>("mode") ?: "fit"
                player.setResizeMode(mapResizeMode(mode))
                result.success(null)
            }
            "replayKeyState" -> {
                player.replayKeyState()
                result.success(null)
            }
            "setAudioTrack" -> {
                val id = call.argument<String>("id") ?: "auto"
                player.setAudioTrack(id)
                result.success(null)
            }
            "setSubtitleTrack" -> {
                val id = call.argument<String>("id") ?: "no"
                player.setSubtitleTrack(id)
                result.success(null)
            }
            "setSubtitleStyle" -> {

                result.success(null)
            }
            "getProperty" -> {
                val name = call.argument<String>("name") ?: ""
                result.success(player.getProperty(name))
            }
            "dispose" -> {
                player.dispose()
                players.remove(playerId)
                result.success(null)
            }
            else -> result.notImplemented()
        }
    }

    private fun mapResizeMode(mode: String): Int {
        return when (mode) {
            "fill" -> Media3AspectRatioFrameLayout.RESIZE_FILL
            "zoom" -> Media3AspectRatioFrameLayout.RESIZE_ZOOM
            else -> Media3AspectRatioFrameLayout.RESIZE_FIT
        }
    }

    companion object {
        private const val TAG = "Media3EnginePlugin"
        private const val CHANNEL_METHOD = "smplayer/media3_engine"
        private const val CHANNEL_EVENTS = "smplayer/media3_engine/events"
        private const val VIEW_TYPE = "smplayer/media3_surface"
    }
}
