package de.kif.backend.util

import de.kif.backend.repository.*
import de.kif.backend.route.api.error
import de.kif.backend.route.api.success
import de.kif.common.Message
import de.kif.common.MessageBox
import de.kif.common.MessageType
import de.kif.common.RepositoryType
import de.kif.common.model.Post
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.html.currentTimeMillis
import kotlin.concurrent.thread

object PushService {

    internal var leastValidTimestamp = System.currentTimeMillis()
    val signature = Post.generateUrl()

    private val messages: MutableList<Pair<Long, Message>> = mutableListOf()

    /**
     * Save the message with the current timestamp
     */
    fun notify(type: MessageType, repository: RepositoryType, id: Long) {
        val timestamp = System.currentTimeMillis()
        val message = Message(type, repository, id)

        synchronized(messages) {
            messages += Pair(timestamp, message)
        }
    }

    private fun getIndexOfTimestamp(timestamp: Long): Int {
        val index = messages.binarySearch(Pair(timestamp, Message.empty), compareBy { it.first })
        val i = if (index < 0) {
            -index - 1
        } else {
            index
        }

        return if (i < 0) 0 else i
    }

    /**
     * Get all messages created after the given timestamp.
     * The return Box has the current timestamp. If the leastValidTimestamp is less then the current timestamp set the
     * valid flag to false and return an empty message list.
     */
    fun getMessages(timestamp: Long): MessageBox {
        return if (timestamp < leastValidTimestamp) {
            MessageBox(
                System.currentTimeMillis(),
                signature,
                false,
                emptyList()
            )
        } else {
            val m = synchronized(messages) {
                val index = getIndexOfTimestamp(timestamp)
                messages.drop(index)
            }

            MessageBox(
                System.currentTimeMillis(),
                signature,
                true,
                m.map { it.second }
            )
        }
    }

    /**
     * Delete all messages before the given timestamp
     */
    fun gc(timestamp: Long) {
        leastValidTimestamp = timestamp

        synchronized(messages) {
            messages.removeIf {
                it.first < timestamp
            }
        }
    }
}

fun Route.pushService() {
    PushService.leastValidTimestamp = System.currentTimeMillis()

    get("/api/updates") {
        try {
            val timestamp = call.request.queryParameters["timestamp"]?.toLongOrNull() ?: 0

            val messageBox = PushService.getMessages(timestamp)

            call.success(messageBox)
        } catch (_: Exception) {
            call.error(HttpStatusCode.InternalServerError)
        }
    }

    RoomRepository.registerPushService()
    ScheduleRepository.registerPushService()
    TrackRepository.registerPushService()
    UserRepository.registerPushService()
    WorkGroupRepository.registerPushService()
    PostRepository.registerPushService()
    AnnouncementRepository.registerPushService()

    thread(
        start = true,
        isDaemon = true,
        name = "PushServiceGC"
    ) {
        while (true) {
            PushService.gc(currentTimeMillis() - 1000 * 60)
            Thread.sleep(1000 * 60 * 5)
        }
    }
}
