package co.id.datacomsolusindo.ipphonebridge

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.springframework.messaging.simp.stomp.StompFrameHandler
import org.springframework.messaging.simp.stomp.StompSession
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import kotlin.system.measureTimeMillis

object ClientHolder {
    private val mutexSucReq = Mutex()
    private val clientMap: ConcurrentMap<String, Client> by lazy { ConcurrentHashMap() }
    private val subsList: ConcurrentMap<String, StompSession.Subscription> by lazy { ConcurrentHashMap() }

    private var onPut: (key: String) -> Unit = {}
    private var onRemove: (key: String) -> Unit = {}

    fun setOnPut(x: (key: String) -> Unit) {
        onPut = x
    }

    fun setOnRemove(x: (key: String) -> Unit) {
        onRemove = x
    }

    private suspend fun massiveRun(action: suspend () -> Unit) {
        measureTimeMillis {
            coroutineScope { // sc
                launch {
                    action()
                }
            }
        }
    }

    fun subsAll(ss: StompSession, handler: StompFrameHandler) {
        clientMap.forEach() {
            subsList.getOrPut(it.key) { ss.subscribe("/topic/partial/${it.key}", handler) }
        }
    }

    fun unSubsAll() {
        subsList.forEach() {
            it.value.unsubscribe()
        }
    }

    fun addSubs(key: String, subs: StompSession.Subscription) {
        subsList.getOrPut(key) { subs }
    }

    fun removeSubs(key: String) {
        subsList.filter { it.key == key }.forEach { it.value.unsubscribe() }
    }


    fun removeBySessionId(sessionId: String?) {
        clientMap.entries.filter { it.value.sessionId == sessionId }.forEach {
            clientMap.remove(it.key)
            onRemove(it.key)
            //unsubscribe "/topic/partial/client"
        }
    }

    fun put(key: String, client: Client) {
        clientMap.getOrPut(key) { client }
        onPut(key)
        //subscribe  "/topic/partial/client"
    }

    fun get() = clientMap

    fun addFailedRequest(clNum: String) {
        runBlocking {
            withContext(Dispatchers.Default) {
                massiveRun {
                    mutexSucReq.withLock {
                        val cl = clientMap[clNum]
                        if (cl != null) {
                            cl.lastRequest =
                                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
                            cl.reqFailed += 1
                        }
                    }
                }
            }
        }
    }

    fun addSuccessRequest(clNum: String, duration: Double) {
        runBlocking {
            withContext(Dispatchers.Default) {
                massiveRun {
                    // protect each increment with lock
                    mutexSucReq.withLock {
                        val cl = clientMap[clNum]
                        if (cl != null) {
                            cl.lastRequest =
                                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
                            cl.avgReqSuccessTime =
                                ((cl.avgReqSuccessTime * cl.reqSuccess) + duration) / (cl.reqSuccess + 1)
                            cl.reqSuccess += 1
                        }
                    }
                }
            }
        }
    }
}