package com.adealink.weparty.config

import com.adealink.frame.base.Rlt
import com.adealink.frame.data.collections.ConcurrentList
import com.adealink.frame.data.json.toJsonErrorNull
import com.adealink.frame.frame.BaseFrame
import com.adealink.frame.frame.IListener
import com.adealink.frame.log.Log
import com.adealink.frame.network.data.Res
import com.adealink.weparty.App
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap

val globalConfigManager: GlobalConfigManager by lazy { GlobalConfigManager() }

class GlobalConfigManager : BaseFrame<IListener>() {


    val configDataCache = GlobalConfigData()
    private val listenerMap =
        ConcurrentHashMap<GlobalConfigType, ConcurrentList<IGlobalConfigListener>>()

    private val configHttpService by lazy {
        App.instance.networkService.getHttpService(ConfigHttpService::class.java)
    }

    private var lastFetchAllConfigTime = 0L
    private val fetchTimeMap = ConcurrentHashMap<GlobalConfigType, Long>()
    private val allConfigTypes = GlobalConfigType.entries

    private val getConfigRunnable by lazy {
        Runnable {
            Log.d(
                TAG_GLOBAL_CONFIG,
                "run schedule get all config, currentTime:${System.currentTimeMillis()}"
            )
            getAllGlobalConfig(false)
            scheduleGetConfig()
        }
    }

    fun getAllGlobalConfig(forceNet: Boolean) {
        if (forceNet) {
            scheduleGetConfig()
        }
        launch {
            if (forceNet || System.currentTimeMillis() - lastFetchAllConfigTime > GET_CONFIG_TIME_INTERVAL) {
                val rlt = suspendGetGlobalConfig(allConfigTypes)
                if (rlt is Rlt.Success) {
                    lastFetchAllConfigTime = System.currentTimeMillis()
                    configDataCache.merge(rlt.data.data?.result)
                }
//                notifyConfigs(copyConfigs())
            }
        }
    }

    private fun scheduleGetConfig() {
        removeFromSerialHandler(getConfigRunnable)
        runOnSerialHandler(getConfigRunnable, GET_CONFIG_TIME_INTERVAL)
    }


    private suspend fun suspendGetGlobalConfig(configs: List<GlobalConfigType>): Rlt<Res<GetGlobalConfigRes>> {
        val rlt =
            configHttpService.getGlobalConfig(GetGlobalConfigReq(configs = configs.map { it.value }))
                .apply {
                    Log.logRltD(TAG_GLOBAL_CONFIG, "suspendGetGlobalConfig", this)
                }
        if (rlt is Rlt.Success) {
            val result = rlt.data.data?.result
            configDataCache.merge(result)
            withContext(this.coroutineContext) {
                GlobalConfigLocalService.configMap = toJsonErrorNull(configDataCache) ?: ""
            }
        }
        return rlt
    }

    inline fun <reified T> get(config: Config<T>): T? {
        return configDataCache.getConfig(config)
    }

    suspend fun <T> getConfig(config: GlobalConfigType): T? {
        val configData = configDataCache.getConfig<T>(config)
        return configData
    }


    fun addListener(configType: GlobalConfigType, l: IGlobalConfigListener) {
        val listeners = listenerMap[configType] ?: ConcurrentList()
        listeners.add(l)
        listenerMap[configType] = listeners
    }

    fun removeListener(configType: GlobalConfigType, l: IGlobalConfigListener) {
        val listeners = listenerMap[configType] ?: ConcurrentList()
        listeners.remove(l)
        listenerMap[configType] = listeners
    }

    private fun notifyConfigs(configType: GlobalConfigType, configs: List<String>) {
        val listeners = listenerMap[configType]
        listeners?.dispatch {
            it.onConfigGet(configType, configs)
        }
    }

    fun logout() {
        launch {
            removeFromSerialHandler(getConfigRunnable)
            lastFetchAllConfigTime = 0
            fetchTimeMap.clear()
//            configMap.clear()
            //登出不清空监听回调
            //listenerMap.clear()
        }
    }
}