package com.adealink.weparty.config

import com.adealink.frame.base.Rlt
import com.adealink.frame.data.collections.ConcurrentList
import com.adealink.frame.data.json.froJsonErrorNull
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.mvvm.livedata.OnceMutableLiveData
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: IGlobalConfigManager by lazy { GlobalConfigManager() }

class GlobalConfigManager : BaseFrame<IListener>(), IGlobalConfigManager {

    private val listenerMap =
        ConcurrentHashMap<GlobalConfigType, ConcurrentList<IGlobalConfigListener>>()
    private val configMap =
        froJsonErrorNull<HashMap<Int, List<String>>>(GlobalConfigLocalService.configMap)
            ?: HashMap()
    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.values().map { it.value }

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

    override 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()
                }
                notifyConfigs(copyConfigs())
            }
        }
    }

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

    override fun getGlobalConfig(forceNet: Boolean, configType: GlobalConfigType): OnceMutableLiveData<List<String>?> {
        val onceMutableLiveData = OnceMutableLiveData<List<String>?>()
        launch {
            if (forceNet || System.currentTimeMillis() - (fetchTimeMap[configType] ?: 0) > GET_CONFIG_TIME_INTERVAL) {
                val oneConfigList = listOf(configType.value)
                val rlt = suspendGetGlobalConfig(oneConfigList)
                if (rlt is Rlt.Success) {
                    fetchTimeMap[configType] = System.currentTimeMillis()
                }
            }
            val config = configMap[configType.value]
            onceMutableLiveData.postValue(config)
            config?.let {
                notifyConfigs(configType, it)
            }
        }
        return onceMutableLiveData
    }

    private suspend fun suspendGetGlobalConfig(configs: List<Int>): Rlt<Res<GetGlobalConfigRes>> {
        val rlt =
            configHttpService.getGlobalConfig(GetGlobalConfigReq(configs = configs))
                .apply {
                    Log.logRltD(TAG_GLOBAL_CONFIG, "suspendGetGlobalConfig", this)
                }
        if (rlt is Rlt.Success) {
            val res = rlt.data.data
            configs.onEach {
                configMap[it] = res?.configsMap?.get(it) ?: emptyList()
            }
            withContext(this.coroutineContext) {
                GlobalConfigLocalService.configMap = toJsonErrorNull(configMap) ?: ""
            }
        }
        return rlt
    }

    override suspend fun suspendGetConfig(configType: GlobalConfigType): List<String>? {
        var configs = configMap[configType.value]
        if (configs.isNullOrEmpty().not()) {
            return configs
        }

        val oneConfigList = listOf(configType.value)
        val rlt =
            configHttpService.getGlobalConfig(GetGlobalConfigReq(configs = oneConfigList))
                .apply {
                    Log.logRltD(
                        TAG_GLOBAL_CONFIG,
                        "suspendGetConfig,type:${configType.value}",
                        this
                    )
                }
        if (rlt is Rlt.Success) {
            configs = rlt.data.data?.configsMap?.get(configType.value)
            if (configs != null) {
                configMap[configType.value] = configs
            }
        }
        return configs
    }

    override suspend fun <T> suspendGetConfig(configType: GlobalConfigType, clazz: Class<T>): T? {
        val configs = suspendGetConfig(configType)
        if (configs.isNullOrEmpty()) {
            return null
        }

        return froJsonErrorNull(configs.firstOrNull(), clazz)
    }

    override fun getConfig(configType: GlobalConfigType): List<String>? {
        return configMap[configType.value]
    }

    override suspend fun <T> suspendGetConfigList(configType: GlobalConfigType, clazz: Class<T>): List<T> {
        val configs = suspendGetConfig(configType)
        if (configs.isNullOrEmpty()) {
            return listOf()
        }

        return configs.mapNotNull { froJsonErrorNull(it, clazz) }.toList()
    }

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

    override 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)
        }
    }

    private suspend fun copyConfigs(): Map<Int, List<String>> {
        return withContext(this.coroutineContext) {
            mutableMapOf<Int, List<String>>().apply { putAll(configMap) }
        }
    }

    private fun notifyConfigs(configsMap: Map<Int, List<String>>) {
        Log.d(TAG_GLOBAL_CONFIG, "notifyConfigs:$configsMap")
        configsMap.forEach { entry ->
            val type = GlobalConfigType.map(entry.key) ?: return@forEach
            val listeners = listenerMap[type]
            listeners?.dispatch {
                it.onConfigGet(type, entry.value)
            }
        }
    }

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