package com.humand.demo.modules.Audio

import android.bluetooth.BluetoothA2dp
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothProfile
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat

/**
 * Observes system audio route / device changes and reports them via callback.
 */
class AudioRouteMonitor(
    private val context: Context,
    private val onRouteChanged: (String, Boolean) -> Unit,
) {

    companion object {
        private const val TAG = "AudioRouteMonitor"
    }

    private val audioManager: AudioManager =
        context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    private var bluetoothA2dp: BluetoothA2dp? = null
    private var isStarted = false
    private var deviceCallbackRegistered = false
    private var commDeviceListener: AudioManager.OnCommunicationDeviceChangedListener? = null

    fun start() {
        if (isStarted) {
            notifyRouteChanged()
            return
        }

        isStarted = true
        Log.d(TAG, "Starting audio route monitor")
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                val listener = AudioManager.OnCommunicationDeviceChangedListener { device ->
                    val routeName = device?.let { routeNameForType(it.type) } ?: "UNKNOWN"
                    notifyRouteChanged(routeName)
                }
                commDeviceListener = listener
                audioManager.addOnCommunicationDeviceChangedListener(
                    ContextCompat.getMainExecutor(context),
                    listener,
                )
            }

            if (!deviceCallbackRegistered) {
                audioManager.registerAudioDeviceCallback(deviceCallback, null)
                deviceCallbackRegistered = true
            }

            bluetoothAdapter?.getProfileProxy(
                context,
                bluetoothProfileListener,
                BluetoothProfile.A2DP,
            )

            val filter = IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)
            context.registerReceiver(bluetoothReceiver, filter)
        } catch (error: Exception) {
            Log.e(TAG, "Error starting audio route monitor", error)
        }

        notifyRouteChanged()
    }

    fun stop() {
        if (!isStarted) {
            return
        }
        isStarted = false
        Log.d(TAG, "Stopping audio route monitor")

        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                commDeviceListener?.let { listener ->
                    try {
                        audioManager.removeOnCommunicationDeviceChangedListener(listener)
                    } catch (error: Exception) {
                        Log.e(TAG, "removeOnCommunicationDeviceChangedListener error", error)
                    }
                }
                commDeviceListener = null
            }

            if (deviceCallbackRegistered) {
                try {
                    audioManager.unregisterAudioDeviceCallback(deviceCallback)
                } catch (_: Exception) {
                }
                deviceCallbackRegistered = false
            }

            bluetoothAdapter?.closeProfileProxy(BluetoothProfile.A2DP, bluetoothA2dp)
            bluetoothA2dp = null

            try {
                context.unregisterReceiver(bluetoothReceiver)
            } catch (_: IllegalArgumentException) {
                // Receiver already unregistered.
            }
        } catch (error: Exception) {
            Log.e(TAG, "Error stopping audio route monitor", error)
        }
    }

    fun getCurrentRoute(): String {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            audioManager.communicationDevice?.let { routeNameForType(it.type) } ?: "UNKNOWN"
        } else {
            @Suppress("DEPRECATION")
            when {
                audioManager.isBluetoothScoOn -> "BLUETOOTH"
                audioManager.isWiredHeadsetOn -> "WIRED_HEADPHONES"
                audioManager.isSpeakerphoneOn -> "SPEAKER"
                else -> "EARPIECE"
            }
        }
    }

    fun hasBluetoothDevice(): Boolean {
        val outputs =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                audioManager.availableCommunicationDevices
            } else {
                audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS).toList()
            }

        return outputs.any { device ->
            device.type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP ||
                device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
        }
    }

    private fun notifyRouteChanged(routeOverride: String? = null) {
        val route = routeOverride ?: getCurrentRoute()
        val hasBluetooth = hasBluetoothDevice()
        onRouteChanged(route, hasBluetooth)
    }

    private val deviceCallback =
        object : AudioDeviceCallback() {
            override fun onAudioDevicesAdded(added: Array<out AudioDeviceInfo>) {
                Log.d(TAG, "Audio devices added")
                notifyRouteChanged()
            }

            override fun onAudioDevicesRemoved(removed: Array<out AudioDeviceInfo>) {
                Log.d(TAG, "Audio devices removed")
                notifyRouteChanged()
            }
        }

    private val bluetoothReceiver =
        object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                intent ?: return
                if (intent.action == BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED) {
                    Log.d(TAG, "Bluetooth connection state changed")
                    notifyRouteChanged()
                }
            }
        }

    private val bluetoothProfileListener =
        object : BluetoothProfile.ServiceListener {
            override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
                if (profile == BluetoothProfile.A2DP) {
                    bluetoothA2dp = proxy as? BluetoothA2dp
                    Log.d(TAG, "Bluetooth A2DP connected")
                    notifyRouteChanged()
                }
            }

            override fun onServiceDisconnected(profile: Int) {
                if (profile == BluetoothProfile.A2DP) {
                    bluetoothA2dp = null
                    Log.d(TAG, "Bluetooth A2DP disconnected")
                    notifyRouteChanged()
                }
            }
        }

    private fun routeNameForType(type: Int): String {
        return when (type) {
            AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
            AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "BLUETOOTH"
            AudioDeviceInfo.TYPE_WIRED_HEADSET,
            AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
            AudioDeviceInfo.TYPE_USB_HEADSET -> "WIRED_HEADPHONES"
            AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "SPEAKER"
            AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "EARPIECE"
            else -> "UNKNOWN"
        }
    }
}
