package com.lanyun.wanglaotou.util

import android.util.Log
import com.google.gson.Gson
import com.lanyun.wanglaotou.AppConfig
import com.lanyun.wanglaotou.dao.ServerAffiliationInfo
import com.lanyun.wanglaotou.dao.ServerConfig
import com.lanyun.wanglaotou.model.NodeModel
import com.lanyun.wanglaotou.model.UrlModel
import com.lanyun.wanglaotou.model.UserModel
import com.lanyun.wanglaotou.model.UserSubModel
import com.tencent.mmkv.MMKV

object MmkvManager {
    const val ID_MAIN = "MAIN"
    const val ID_SERVER_CONFIG = "SERVER_CONFIG"
    const val ID_SELECT_SERVER_CONFIG = "SELECT_SERVER_CONFIG"
    const val ID_USER_CONFIG = "USER_CONFIG"
    const val ID_SERVER_RAW = "SERVER_RAW"
    const val ID_SERVER_AFF = "SERVER_AFF"
    const val ID_SUB = "SUB"
    const val ID_SETTING = "SETTING"
    const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
    const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
    const val KEY_SELECTED_NODE = "SELECTED_NODE"
    const val KEY_USER_INFO = "USER_INFO"
    const val KEY_USER_SUB_INFO = "USER_SUB_INFO"
    const val KEY_URL_INFO = "URL_INFO"
    private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) }
    private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
    private val selectServerStorage by lazy { MMKV.mmkvWithID(ID_SELECT_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
    private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
    private val userInfoStorage by lazy { MMKV.mmkvWithID(ID_USER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
    private val settingsStorage by lazy { MMKV.mmkvWithID(ID_SETTING, MMKV.MULTI_PROCESS_MODE) }

    fun decodeServerAffiliationInfo(guid: String): ServerAffiliationInfo? {
        if (guid.isBlank()) {
            return null
        }
        val json = serverAffStorage?.decodeString(guid)
        if (json.isNullOrBlank()) {
            return null
        }
        return Gson().fromJson(json, ServerAffiliationInfo::class.java)
    }

    fun encodeServerTestDelayMillis(guid: String, testResult: Long) {
        if (guid.isBlank()) {
            return
        }
        val aff = decodeServerAffiliationInfo(guid) ?: ServerAffiliationInfo()
        aff.testDelayMillis = testResult
        serverAffStorage?.encode(guid, Gson().toJson(aff))
    }

    fun clearAllTestDelayResults() {
        serverAffStorage?.allKeys()?.forEach { key ->
            decodeServerAffiliationInfo(key)?.let { aff ->
                aff.testDelayMillis = 0
                serverAffStorage?.encode(key, Gson().toJson(aff))
            }
        }
    }

    fun decodeSelectServer(): NodeModel? {
        val json = selectServerStorage?.decodeString(KEY_SELECTED_NODE)
        if (json.isNullOrBlank()) {
            return null
        }

        return Gson().fromJson(json, NodeModel::class.java)
    }

    fun encodeSelectServer(nodeModel: NodeModel){
        selectServerStorage?.encode(KEY_SELECTED_NODE, Gson().toJson(nodeModel))
    }

    fun decodeUserInfo(): UserModel? {
        val json = userInfoStorage?.decodeString(KEY_USER_INFO)
        if (json.isNullOrBlank()) {
            return null
        }

        return Gson().fromJson(json, UserModel::class.java)
    }

    fun encodeUserinfo(userModel: UserModel){
        userInfoStorage?.encode(KEY_USER_INFO, Gson().toJson(userModel))
    }


    fun decodeSubUserInfo(): UserSubModel? {
        val json = userInfoStorage?.decodeString(KEY_USER_SUB_INFO)
        if (json.isNullOrBlank()) {
            return null
        }

        return Gson().fromJson(json, UserSubModel::class.java)
    }

    fun encodeSubUserinfo(userSubModel: UserSubModel){
        userInfoStorage?.encode(KEY_USER_SUB_INFO, Gson().toJson(userSubModel))
    }

    fun decodeUrlInfo(): UrlModel? {
        val json = userInfoStorage?.decodeString(KEY_URL_INFO)
        if (json.isNullOrBlank()) {
            return null
        }
        return Gson().fromJson(json, UrlModel::class.java)
    }

    fun encodeUrlinfo(userSubModel: UrlModel){
        userInfoStorage?.encode(KEY_URL_INFO, Gson().toJson(userSubModel))
    }


    fun decodeServerConfig(guid: String): ServerConfig? {
        if (guid.isBlank()) {
            return null
        }
        val json = serverStorage?.decodeString(guid)
        if (json.isNullOrBlank()) {
            return null
        }
        return Gson().fromJson(json, ServerConfig::class.java)
    }

    fun decodeServerList(): MutableList<String> {
        val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
        return if (json.isNullOrBlank()) {
            mutableListOf()
        } else {
            Gson().fromJson(json, Array<String>::class.java).toMutableList()
        }
    }


    fun encodeServerConfig(guid: String, config: ServerConfig): String {
        val key = guid.ifBlank { Utils.getUuid() }
        serverStorage?.encode(key, Gson().toJson(config))
        val serverList = decodeServerList()
        if (!serverList.contains(key)) {
            serverList.add(key)
            mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
            if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) {
                mainStorage?.encode(KEY_SELECTED_SERVER, key)
            }
        }
        return key
    }


    fun removeServer(guid: String) {
        if (guid.isBlank()) {
            return
        }
        if (mainStorage?.decodeString(KEY_SELECTED_SERVER) == guid) {
            mainStorage?.remove(KEY_SELECTED_SERVER)
        }
        val serverList = decodeServerList()
        serverList.remove(guid)
        mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
        serverStorage?.remove(guid)
        serverAffStorage?.remove(guid)
    }
    fun removeAllServer() {
        mainStorage?.clearAll()
        serverStorage?.clearAll()
        serverAffStorage?.clearAll()
    }



    fun onAppConfigChanged(key: String?,value: Any?) {
        Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key ---- value---> $value")
        when(key) {
            AppConfig.PREF_MODE,
            AppConfig.PREF_VPN_DNS,
            AppConfig.PREF_REMOTE_DNS,
            AppConfig.PREF_DOMESTIC_DNS,
            AppConfig.PREF_LOCAL_DNS_PORT,
            AppConfig.PREF_SOCKS_PORT,
            AppConfig.PREF_HTTP_PORT,
            AppConfig.PREF_LOGLEVEL,
            AppConfig.PREF_LANGUAGE,
            AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
            AppConfig.PREF_ROUTING_MODE,
            AppConfig.PREF_V2RAY_ROUTING_AGENT,
            AppConfig.PREF_V2RAY_ROUTING_BLOCKED,
            AppConfig.PREF_V2RAY_ROUTING_DIRECT, -> {
                settingsStorage?.encode(key, value.toString())
            }
            AppConfig.PREF_SPEED_ENABLED,
            AppConfig.PREF_PROXY_SHARING,
            AppConfig.PREF_LOCAL_DNS_ENABLED,
            AppConfig.PREF_FAKE_DNS_ENABLED,
            AppConfig.PREF_ALLOW_INSECURE,
            AppConfig.PREF_PREFER_IPV6,
            AppConfig.PREF_PER_APP_PROXY,
            AppConfig.PREF_BYPASS_APPS,
            AppConfig.PREF_CONFIRM_REMOVE, -> {
                settingsStorage?.encode(key, value.toString().toBoolean())
            }
            AppConfig.PREF_SNIFFING_ENABLED -> {
                settingsStorage?.encode(key, value.toString().toBoolean())
            }
            AppConfig.PREF_PER_APP_PROXY_SET -> {
                settingsStorage?.encode(key, value.toString())
            }
        }
    }
}