package com.anypreta.bountyboard.data.push

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.anypreta.bountyboard.BountyBoardApp
import com.anypreta.bountyboard.ui.util.htmlToPlain
import com.anypreta.bountyboard.ui.util.parseInstant
import kotlinx.coroutines.flow.first

/**
 * Periodic background poll: fetches unread notifications + conversations and raises
 * a system notification for anything new since the last run. Runs in the app process
 * even when the UI is closed; auth rides on the persisted cookie jar.
 */
class PushWorker(
    appContext: Context,
    params: WorkerParameters,
) : CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        val app = applicationContext as? BountyBoardApp ?: return Result.success()
        val container = app.container
        val state = PushStateStore(applicationContext)

        // Respect the user toggle and an existing login.
        if (!container.settings.pushEnabled.first()) return Result.success()
        val host = container.currentHost() ?: return Result.success()
        if (!container.cookieJar.hasSession(host)) return Result.success()

        val seeded = state.seeded()
        var lastNotifId = state.lastNotificationId()
        val convSeen = state.convSeen().toMutableMap()

        // ---- Alerts ----
        try {
            val res = container.repo.call { notifications(unread = true, limit = 25) }
            val unread = res.notifications.filter { it.isUnread }
            // Newest first from the server; notify oldest→newest for sensible ordering.
            val fresh = unread
                .filter { lastNotifId == null || it.id > lastNotifId!! }
                .sortedBy { it.id }
            if (seeded) {
                fresh.forEach { n ->
                    PushNotifier.notifyAlert(
                        applicationContext,
                        id = ("a" + n.id).hashCode(),
                        title = n.title.ifBlank { "New alert" },
                        body = htmlToPlain(n.body).ifBlank { "You have a new alert." },
                        route = PushNotifier.routeForServerLink(n.link),
                    )
                }
            }
            unread.maxByOrNull { it.id }?.let { newest ->
                if (lastNotifId == null || newest.id > lastNotifId!!) lastNotifId = newest.id
            }
        } catch (_: Exception) {
            // network/auth hiccup — try again next cycle
        }

        // ---- Messages ----
        try {
            val res = container.repo.call { conversations() }
            res.conversations.forEach { c ->
                val last = c.lastMessageAt
                val prev = convSeen[c.id]
                val isNew = last != null && (prev == null || isAfter(last, prev))
                if (seeded && c.unread > 0 && isNew) {
                    val title = c.title?.ifBlank { null } ?: "New message"
                    val body = if (c.unread == 1) "New message" else "${c.unread} new messages"
                    PushNotifier.notifyMessage(
                        applicationContext,
                        id = ("m" + c.id).hashCode(),
                        title = title,
                        body = body,
                        route = "conversation/${c.id}",
                    )
                }
                if (last != null) convSeen[c.id] = last
            }
        } catch (_: Exception) {
        }

        state.update(seeded = true, lastNotificationId = lastNotifId, convSeen = convSeen)
        return Result.success()
    }

    private fun isAfter(a: String, b: String): Boolean {
        val da = parseInstant(a)
        val db = parseInstant(b)
        return if (da != null && db != null) da.after(db) else a > b
    }
}
