package eu.darken.bluemusic.monitor.ui

import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.bluemusic.R
import eu.darken.bluemusic.common.PendingIntentCompat
import eu.darken.bluemusic.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.bluemusic.common.debug.logging.log
import eu.darken.bluemusic.common.debug.logging.logTag
import eu.darken.bluemusic.common.hasApiLevel
import eu.darken.bluemusic.devices.core.ManagedDevice
import eu.darken.bluemusic.main.ui.MainActivity
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
import javax.inject.Singleton


@Singleton
class MonitorNotifications @Inject constructor(
    @ApplicationContext private val context: Context,
    private val notificationManager: NotificationManager,
) {

    private val builderLock = Mutex()
    private val builder: NotificationCompat.Builder

    init {
        ensureNotificationChannel(context)

        val openIntent = Intent(context, MainActivity::class.java)
        val openPi = PendingIntent.getActivity(context, 0, openIntent, PendingIntentCompat.FLAG_IMMUTABLE)

        builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
            .setChannelId(NOTIFICATION_CHANNEL_ID)
            .setContentIntent(openPi)
            .setSmallIcon(R.drawable.ic_notification_small)


    }

    private fun getBuilder(
        devices: Collection<ManagedDevice>,
    ): NotificationCompat.Builder = builder.apply {
        log(TAG, VERBOSE) { "getBuilder(${devices.map { "${it.address}/${it.label}" }})" }

        if (devices.isNotEmpty()) {
            val sb = StringBuilder()
            devices.forEachIndexed { index, device ->
                sb.append(device.label)
                if (index < devices.size - 1) sb.append(", ")
            }
            setContentTitle(sb.toString())

            val extraFlags = mutableListOf<String>()
            var listening = false
            var locking = false
            var waking = false
            var limiting = false

            for (dev in devices) {
                if (!dev.isActive) continue

                if (!listening && dev.volumeObservingEffective) {
                    listening = true
                    log(TAG) { "Keep running because we are observing changes" }
                    extraFlags.add(context.getString(R.string.label_volume_listener))
                }
                if (!locking && dev.volumeLock) {
                    locking = true
                    log(TAG) { "Keep running because the device wants volume lock: ${dev.address}/${dev.label}" }
                    extraFlags.add(context.getString(R.string.devices_device_config_volume_lock_label))
                }
                if (!waking && dev.keepAwake) {
                    waking = true
                    log(TAG) { "Keep running because the device wants keep awake: ${dev.address}/${dev.label}" }
                    extraFlags.add(context.getString(R.string.devices_device_config_keep_awake_label))
                }
                if (!limiting && dev.volumeRateLimiterEffective) {
                    limiting = true
                    log(TAG) { "Keep running because the device to be rate limited: ${dev.address}/${dev.label}" }
                    extraFlags.add(context.getString(R.string.devices_device_config_volume_rate_limiter_label))
                }
            }
            val msg = extraFlags.joinToString(", ")
            setContentText(msg)
            setStyle(NotificationCompat.BigTextStyle().bigText(msg))
        } else {
            setContentTitle(context.getString(R.string.label_no_connected_devices))
            setContentText("")
            setStyle(NotificationCompat.BigTextStyle().bigText(""))
        }

        clearActions()
        val stopIntent = Intent(ACTION_STOP_MONITOR).apply { setPackage(context.packageName) }
        val stopPi = PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntentCompat.FLAG_IMMUTABLE)
        addAction(NotificationCompat.Action.Builder(0, context.getString(R.string.action_exit), stopPi).build())
    }

    suspend fun getDevicesNotification(devices: Collection<ManagedDevice>): Notification = builderLock.withLock {
        log(TAG, VERBOSE) { "getDevicesNotification(${devices.map { "${it.address}/${it.label}" }})" }
        return@withLock getBuilder(devices).build()
    }

    companion object {
        val TAG = logTag("Monitor", "Notifications")
        private const val NOTIFICATION_CHANNEL_ID = "notification.channel.core"
        internal const val NOTIFICATION_ID = 1
        const val ACTION_STOP_MONITOR = "eu.darken.bluemusic.monitor.STOP"

        @SuppressLint("NewApi")
        private fun ensureNotificationChannel(context: Context) {
            if (hasApiLevel(Build.VERSION_CODES.O)) {
                val nm = context.getSystemService(NotificationManager::class.java) as NotificationManager
                nm.createNotificationChannel(
                    NotificationChannel(
                        NOTIFICATION_CHANNEL_ID,
                        context.getString(R.string.label_notification_channel_status),
                        NotificationManager.IMPORTANCE_MIN
                    )
                )
            }
        }

        fun createEarlyNotification(context: Context): Notification {
            ensureNotificationChannel(context)

            val openIntent = Intent(context, MainActivity::class.java)
            val openPi = PendingIntent.getActivity(context, 0, openIntent, PendingIntentCompat.FLAG_IMMUTABLE)

            return NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification_small)
                .setContentTitle(context.getString(R.string.app_name))
                .setContentText(context.getString(R.string.monitor_notification_starting))
                .setContentIntent(openPi)
                .build()
        }
    }
}
