package com.smplayer.smplayer

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
import android.os.Handler
import android.os.Looper
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

class SystemAudioBridge(
    private val context: Context,
    flutterEngine: FlutterEngine,
) {
    companion object {
        private const val METHOD_CHANNEL = "smplayer/system_audio"
        private const val EVENT_CHANNEL = "smplayer/system_audio_events"
        private const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"
        private const val STREAM_TYPE = AudioManager.STREAM_MUSIC
    }

    private val audioManager: AudioManager =
        context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

    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())

    @Volatile
    var keyInterceptEnabled: Boolean = false
        private set

    private val volumeReceiver = object : BroadcastReceiver() {
        override fun onReceive(ctx: Context?, intent: Intent?) {
            if (intent?.action == VOLUME_CHANGED_ACTION) {
                val streamType = intent.getIntExtra(
                    "android.media.EXTRA_VOLUME_STREAM_TYPE", -1
                )
                if (streamType == STREAM_TYPE) {
                    postVolumeEvent()
                }
            }
        }
    }

    init {
        methodChannel.setMethodCallHandler { call, result ->
            when (call.method) {
                "getVolume" -> result.success(currentVolumeMap())
                "adjustVolume" -> {
                    val delta = (call.argument<Int>("delta") ?: 0)
                    adjustVolume(delta, showSystemUi = false)
                    result.success(currentVolumeMap())
                }
                "setKeyIntercept" -> {
                    keyInterceptEnabled = call.argument<Boolean>("enabled") ?: false
                    result.success(null)
                }
                else -> result.notImplemented()
            }
        }
        eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
            override fun onListen(arguments: Any?, sink: EventChannel.EventSink?) {
                eventSink = sink
                context.registerReceiver(
                    volumeReceiver, IntentFilter(VOLUME_CHANGED_ACTION)
                )
            }

            override fun onCancel(arguments: Any?) {
                try {
                    context.unregisterReceiver(volumeReceiver)
                } catch (_: IllegalArgumentException) {
                }
                eventSink = null
            }
        })
    }

    fun adjustVolume(delta: Int, showSystemUi: Boolean) {
        val direction = when {
            delta > 0 -> AudioManager.ADJUST_RAISE
            delta < 0 -> AudioManager.ADJUST_LOWER
            else -> return
        }
        val flag = if (showSystemUi) AudioManager.FLAG_SHOW_UI else 0
        repeat(kotlin.math.abs(delta).coerceAtMost(20)) {
            audioManager.adjustStreamVolume(STREAM_TYPE, direction, flag)
        }
        postVolumeEvent()
    }

    private fun currentVolumeMap(): Map<String, Int> = mapOf(
        "current" to audioManager.getStreamVolume(STREAM_TYPE),
        "max" to audioManager.getStreamMaxVolume(STREAM_TYPE),
    )

    private fun postVolumeEvent() {
        val payload = currentVolumeMap()
        mainHandler.post {
            eventSink?.success(payload)
        }
    }

    fun dispose() {
        try {
            context.unregisterReceiver(volumeReceiver)
        } catch (_: IllegalArgumentException) {
        }
        methodChannel.setMethodCallHandler(null)
        eventChannel.setStreamHandler(null)
        eventSink = null
    }
}
