package com.example.demo.controller

import com.example.demo.model.Message
import com.example.demo.model.User
import kotlinx.coroutines.*
import org.slf4j.LoggerFactory
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.handler.annotation.Payload
import org.springframework.messaging.rsocket.RSocketRequester
import org.springframework.messaging.rsocket.annotation.ConnectMapping
import org.springframework.stereotype.Controller
import io.vavr.collection.Map
import org.springframework.messaging.handler.annotation.MessageExceptionHandler

@Controller
class RsocketConnectionControllerAsync : CoroutineScope {
    private val log = LoggerFactory.getLogger(RsocketConnectionControllerAsync::class.java)
    private val job = Job()
    override val coroutineContext = Dispatchers.Unconfined + job

    @ObsoleteCoroutinesApi
    private val myRsocketActor = rsocketActor()

    @ObsoleteCoroutinesApi
    suspend fun addRequester(rSocketRequester: RSocketRequester, clientId: String) {
        log.info("adding requester {}", clientId)
        myRsocketActor.send(AddRequester(rSocketRequester, clientId))
    }

    @ObsoleteCoroutinesApi
    suspend fun removeRequester(clientId: String) {
        log.info("removing requester {}", clientId)
        myRsocketActor.send(RemoveRequester(clientId))
    }

    @ObsoleteCoroutinesApi
    @ConnectMapping(value = ["client-id2"])
    fun onConnect(
            rSocketRequester: RSocketRequester,
            @Payload user: User
    ) {
        launch(coroutineContext) {
            val clientId = user.id.toString()
            addRequester(rSocketRequester, clientId)
            rSocketRequester
                    .rsocket()
                    .onClose()
                    .subscribe(null, null, {
                        log.info("{} just disconnected", clientId)
                        launch(coroutineContext) { removeRequester(clientId) }
                    })

        }
    }

    @ObsoleteCoroutinesApi
    @MessageMapping("private.news.2")
    fun privateNews(message: Message) {
        launch {
            val res = CompletableDeferred<Map<String, RSocketRequester>>()
            myRsocketActor.send(GetRequesters(res))
            res
                    .await()
                    .filterKeys { key -> key == message.toUser || key == message.fromUser }
                    .values()
                    .forEach { requester ->
                        println("Sending message")
                        sendMessage(requester, message)
                    }
        }
    }

    private fun sendMessage(requester: RSocketRequester, message: Message) =
            requester
                    .route("user.queue.reply")
                    .data(message)
                    .send()
                    .subscribe()

    @MessageExceptionHandler
    suspend fun handleException(ex: IllegalArgumentException): String {
        delay(10)
        return "${ex.message} handled"
    }

}
