package net.buzzert.kordophonedroid

import android.Manifest
import android.R
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.IBinder
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri
import androidx.navigation.NavDeepLinkBuilder
import androidx.navigation.NavDeepLinkRequest
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import net.buzzert.kordophone.backend.model.Message
import net.buzzert.kordophone.backend.server.ChatRepository
import net.buzzert.kordophonedroid.ui.Destination
import javax.inject.Inject

const val PUSH_CHANNEL_ID = "net.buzzert.kordophone.persistentNotification"
const val NEW_MESSAGE_CHANNEL_ID = "net.buzzert.kordophone.newMessage"

const val UPDATER_LOG = "UpdateService"

@AndroidEntryPoint
class UpdateMonitorService: Service()
{
    @Inject lateinit var chatRepository: ChatRepository

    private var newMessageID: Int = 0
    private var watchJob: Job? = null
    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.IO + job)

    private fun createNotificationChannel(channelId: String, channelName: String) {
        val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
        chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        chan.lightColor = Color.BLUE

        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(chan)
    }

    override fun onCreate() {
        super.onCreate()

        Log.v(UPDATER_LOG, "UpdateMonitor onCreate: Begin watching for updates.")

        createNotificationChannel(NEW_MESSAGE_CHANNEL_ID, "New Messages")

        // Connect to monitor
        chatRepository.beginWatchingForUpdates(scope)

        // Connect to new message flow for notifications
        watchJob?.cancel()
        watchJob = scope.launch {
            chatRepository.newMessages.collectLatest(::onReceiveNewMessage)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        createNotificationChannel(PUSH_CHANNEL_ID, "Update Monitor Service")

        val notificationIntent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
            this,
            0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
        )

        val notification: Notification = NotificationCompat.Builder(this, PUSH_CHANNEL_ID)
            .setContentTitle("Kordophone Connected")
            .setContentText("Kordophone is listening for new messages.")
            .setSmallIcon(R.drawable.sym_action_chat)
            .setContentIntent(pendingIntent)
            .setShowWhen(false)
            .setSilent(true)
            .setOngoing(true)
            .build()

        startForeground(5738, notification)

        // Restart if we get killed
        return START_STICKY
    }

    private fun onReceiveNewMessage(message: Message) {
        val hasPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
        if (hasPermission != PackageManager.PERMISSION_GRANTED) {
            Log.e(UPDATER_LOG, "No permissions to post notifications.")
            return
        }

        if (message.conversation.unreadCount == 0) {
            // Not unread.
            Log.v(UPDATER_LOG, "Ignoring read message.")
            return
        }

        if (message.sender == null) {
            // From me.
            Log.v(UPDATER_LOG, "Ignoring message from me.")
            return
        }

        if (message.conversation.isGroupChat) {
            // For now, since these can be noisy and there's no UI for changing it, ignore group chats.
            Log.v(UPDATER_LOG, "Ignoring group chat message.")
            return
        }

        val guid = message.conversation.guid
        val deepLinkIntent = Intent(
            Intent.ACTION_VIEW,
            "kordophone://messages/$guid".toUri(),
            this,
            MainActivity::class.java
        )

        val pendingIntent = PendingIntent.getActivity(this, 0, deepLinkIntent, PendingIntent.FLAG_IMMUTABLE)

        val groupId = message.conversation.guid
        val notification = NotificationCompat.Builder(this, NEW_MESSAGE_CHANNEL_ID)
            .setContentTitle(message.sender)
            .setContentText(message.text)
            .setSmallIcon(R.drawable.stat_notify_chat)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setContentIntent(pendingIntent)
            .setGroup(groupId)
            .build()

        val manager = NotificationManagerCompat.from(this)
        manager.notify(newMessageID++, notification)
    }

    override fun onBind(intent: Intent?): IBinder? {
        // no binding
        return null
    }

    override fun onDestroy() {
        super.onDestroy()

        chatRepository.stopWatchingForUpdates()
        job.cancel()
    }
}