package com.matchlivetv.match_live_tv

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat

/** Mantiene in foreground la diretta (notifica + wake lock). Il motore RTMP vive nel plugin. */
class StreamingForegroundService : Service() {

    private val binder = LocalBinder()
    private val mainHandler = Handler(Looper.getMainLooper())

    private var wakeLock: PowerManager.WakeLock? = null
    private var sessionStartedAtMs: Long = 0L
    private var autoStopTriggered = false

    private val sessionTimeoutRunnable = Runnable {
        if (!autoStopTriggered) {
            autoStopTriggered = true
            ServiceEventBus.emit(
                mapOf(
                    "type" to "stopped",
                    "reason" to "session_timeout",
                    "durationMs" to elapsedMs(),
                ),
            )
            stopForegroundInternal("session_timeout")
        }
    }

    inner class LocalBinder : Binder() {
        fun getService(): StreamingForegroundService = this@StreamingForegroundService
    }

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when (intent?.action) {
            ACTION_START -> startForegroundSession()
            ACTION_STOP -> stopForegroundInternal("user_stop")
        }
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder = binder

    override fun onDestroy() {
        stopForegroundInternal("service_destroy")
        super.onDestroy()
    }

    fun getMetrics(): Map<String, Any?> =
        StreamingEngineHolder.engine?.getMetrics()?.toMap() ?: emptyMap()

    private fun startForegroundSession() {
        autoStopTriggered = false
        sessionStartedAtMs = SystemClock.elapsedRealtime()
        acquireWakeLock()
        promoteToForeground(getString(R.string.streaming_notification_active))
        mainHandler.removeCallbacks(sessionTimeoutRunnable)
        mainHandler.postDelayed(sessionTimeoutRunnable, SESSION_MAX_MS)
    }

    private fun stopForegroundInternal(reason: String) {
        mainHandler.removeCallbacks(sessionTimeoutRunnable)
        releaseWakeLock()
        if (sessionStartedAtMs > 0L) {
            ServiceEventBus.emit(
                mapOf(
                    "type" to "stopped",
                    "reason" to reason,
                    "durationMs" to elapsedMs(),
                ),
            )
        }
        sessionStartedAtMs = 0L
        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
    }

    private fun elapsedMs(): Long {
        return if (sessionStartedAtMs > 0L) {
            SystemClock.elapsedRealtime() - sessionStartedAtMs
        } else {
            0L
        }
    }

    private fun promoteToForeground(content: String) {
        val notification = buildNotification(content)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ServiceCompat.startForeground(
                this,
                NOTIFICATION_ID,
                notification,
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA or
                    ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE,
            )
        } else {
            startForeground(NOTIFICATION_ID, notification)
        }
    }

    fun updateNotificationForState(state: StreamingState) {
        val content = when (state) {
            StreamingState.CONNECTING -> getString(R.string.streaming_notification_connecting)
            StreamingState.STREAMING -> getString(R.string.streaming_notification_streaming)
            StreamingState.RECONNECTING -> getString(R.string.streaming_notification_reconnecting)
            StreamingState.ERROR -> getString(R.string.streaming_notification_error)
            else -> getString(R.string.streaming_notification_active)
        }
        val manager = getSystemService(NotificationManager::class.java)
        manager.notify(NOTIFICATION_ID, buildNotification(content))
    }

    private fun buildNotification(content: String): Notification {
        val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
        val contentIntent = PendingIntent.getActivity(
            this,
            0,
            launchIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        )
        val stopIntent = PendingIntent.getService(
            this,
            1,
            Companion.stopIntent(this),
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        )

        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle(getString(R.string.streaming_notification_title))
            .setContentText(content)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setOngoing(true)
            .setContentIntent(contentIntent)
            .addAction(
                android.R.drawable.ic_media_pause,
                getString(R.string.streaming_notification_stop),
                stopIntent,
            )
            .setCategory(NotificationCompat.CATEGORY_SERVICE)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            return
        }
        val manager = getSystemService(NotificationManager::class.java)
        val channel = NotificationChannel(
            CHANNEL_ID,
            getString(R.string.streaming_notification_channel),
            NotificationManager.IMPORTANCE_LOW,
        ).apply {
            description = getString(R.string.streaming_notification_channel_desc)
        }
        manager.createNotificationChannel(channel)
    }

    private fun acquireWakeLock() {
        if (wakeLock?.isHeld == true) {
            return
        }
        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        wakeLock = powerManager.newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK,
            "$packageName:StreamingWakeLock",
        ).apply {
            setReferenceCounted(false)
            acquire(SESSION_MAX_MS + 60_000L)
        }
    }

    private fun releaseWakeLock() {
        wakeLock?.let { lock ->
            if (lock.isHeld) {
                lock.release()
            }
        }
        wakeLock = null
    }

    companion object {
        const val ACTION_START = "com.matchlivetv.match_live_tv.action.START_STREAM"
        const val ACTION_STOP = "com.matchlivetv.match_live_tv.action.STOP_STREAM"

        private const val CHANNEL_ID = "match_live_tv_streaming"
        private const val NOTIFICATION_ID = 1001
        private val SESSION_MAX_MS = 90L * 60L * 1000L

        fun startIntent(context: Context): Intent {
            return Intent(context, StreamingForegroundService::class.java).apply {
                action = ACTION_START
            }
        }

        fun stopIntent(context: Context): Intent {
            return Intent(context, StreamingForegroundService::class.java).apply {
                action = ACTION_STOP
            }
        }
    }
}

object ServiceEventBus {
    private val listeners = mutableSetOf<(Map<String, Any?>) -> Unit>()

    fun addListener(listener: (Map<String, Any?>) -> Unit) {
        synchronized(listeners) {
            listeners.add(listener)
        }
    }

    fun removeListener(listener: (Map<String, Any?>) -> Unit) {
        synchronized(listeners) {
            listeners.remove(listener)
        }
    }

    fun emit(event: Map<String, Any?>) {
        val snapshot = synchronized(listeners) { listeners.toList() }
        snapshot.forEach { it(event) }
    }
}
