package com.dlc.eboard.mqtt

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.widget.Toast
import com.dlc.eboard.common.CommonUtils
import com.dlc.eboard.common.Constants
import com.dlc.eboard.dummy.DummyData
import com.dlc.eboard.mqtt.entity.*
import com.dlc.eboard.mqtt.event.*
import com.dlc.eboard.sp.EnvConfig
import com.dlc.eboard.sp.EnvSp
import com.google.gson.Gson
import com.rc.core.log.RcLog
import com.rc.httpcore.client.ApiRepository
import io.reactivex.disposables.CompositeDisposable
import org.eclipse.paho.client.mqttv3.MqttMessage
import org.greenrobot.eventbus.EventBus
import java.lang.ref.WeakReference

/**
 * info
 *
 * @author ReiChin_
 */
class MqttManager private constructor() {

    companion object {
        val instance: MqttManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            MqttManager()
        }
    }

    private val mHandler = Handler()
    private val mGson = Gson()

    private lateinit var clientId: String
    private lateinit var subId: String
    private lateinit var mMqttWorker: MqttWorker
    private lateinit var androidId: String
    private lateinit var appContext: Context
    private val weakHandler by lazy { WeakReferenceHandler(this) }
    fun initMqtt(subId: String, androidId: String, appContext: Context) {
        RcLog.info("[MQTT] MqttManager init start.")
        this.clientId = "mqtt_eboard${if (Constants.Mock.MQTT_CLIENT_TEST) "_test" else ""}#$androidId"
        this.subId = subId
        this.androidId = androidId
        this.appContext = appContext
        mMqttWorker = MqttWorker(
            Constants.MqttConfig.SERVER_URI,
            clientId,
            Constants.MqttConfig.U_NAME,
            Constants.MqttConfig.U_PWD
        )
        mMqttWorker.setListener(object : MqttWorker.Listener {

            override fun onConnectSuccess() {
                RcLog.info("[MQTT] MqttManager 连接成功.")
                //  连接成功后，订阅主题
                val topics = arrayOf(
                    Constants.MqttConfig.Topic.HEART,
                    Constants.MqttConfig.Topic.APP_UPDATE.plus(androidId),
                    Constants.MqttConfig.Topic.BULLETIN_BOARD.plus(subId),
                    Constants.MqttConfig.Topic.WARNING.plus(subId),
                    Constants.MqttConfig.Topic.EMERGENCY.plus(subId),
                    Constants.MqttConfig.Topic.THINGS.plus(subId),
                    Constants.MqttConfig.Topic.ENV_UPDATE.plus(androidId),
                    Constants.MqttConfig.Topic.CHECK_MACHINE.plus(subId),
                    Constants.MqttConfig.Topic.NOTICE_MSG.plus(subId),
                    Constants.MqttConfig.Topic.BANNER_IMG,
                    Constants.MqttConfig.Topic.LAB_INFO.plus(subId),
                    Constants.MqttConfig.Topic.SYS_IMG
                )
                // 订阅主题
                mMqttWorker.subscribe(topics, 0)
                // 发送心跳
                mHandler.removeCallbacks(mRunSendHeartBeat)
                mHandler.postDelayed(mRunSendHeartBeat, 2000)

                if (Constants.Mock.MQTT_TEST) {
                    mDummyHandler.removeCallbacks(mDummySendRunnable)
                    mHandler.postDelayed(mDummySendRunnable, 2000)
                }
            }

            override fun messageArrived(topic: String?, message: MqttMessage?) {
//                RcLog.info("[MQTT] MqttManager 收到消息,topic=${topic}, message=${message}")
                dispatchMessage(topic, message.toString())
            }
        })
    }

    fun connect() {
        mMqttWorker.connect()
    }

    /**
     * 释放单例, 及其所引用的资源
     */
    @Synchronized
    fun release() {
        try {
            mDummyHandler.removeCallbacksAndMessages(null)
            mHandler.removeCallbacksAndMessages(null)
            mMqttWorker.setListener(null)
            mMqttWorker.release()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 发送心跳
     */
    private val mRunSendHeartBeat = object : Runnable {
        override fun run() {
            val heartBeatEntry = HeartBeatEntity()
                .apply {
                    type = "heart"
                    macno = clientId
                    time = (System.currentTimeMillis() / 1000).toInt()
                }

            val message = mGson.toJson(heartBeatEntry)
            mMqttWorker.publish(Constants.MqttConfig.Topic.HEART, message, 0)
            mHandler.postDelayed(this, 30000)
        }
    }

    /**
     * 分发消息
     *
     * @param topic
     * @param message
     */
    private fun dispatchMessage(topic: String?, message: String) {
        try {
            when (topic) {
                Constants.MqttConfig.Topic.APP_UPDATE.plus(androidId) -> {
                    val entity = mGson.fromJson(message, UpdateEntity::class.java)
                    EventBus.getDefault().post(UpdateEvent(entity.data))
                }
                Constants.MqttConfig.Topic.BULLETIN_BOARD.plus(subId) -> {
                    val entity = mGson.fromJson(message, BulletinBoardEntity::class.java)
                    EventBus.getDefault().post(BulletinBoardEvent(entity.data))
                }
                Constants.MqttConfig.Topic.WARNING.plus(subId) -> {
                    handleWarning()
                }
                Constants.MqttConfig.Topic.EMERGENCY.plus(subId) -> {
                    val entity = mGson.fromJson(message, EmergencyEntity::class.java)
                    EventBus.getDefault().post(EmergencyEvent(entity.data.context, entity.data.type))
                }
                Constants.MqttConfig.Topic.THINGS.plus(subId) -> {
                    EventBus.getDefault().post(ThingsEvent())
                }
                Constants.MqttConfig.Topic.ENV_UPDATE.plus(androidId) -> {
                    // 更新配置信息
                    if (updateEnvConfig(message)) {
                        weakHandler.sendEmptyMessageDelayed(1, 1000)
                        weakHandler.removeCallbacksAndMessages(null)
//                        object : Handler(Looper.getMainLooper()) {
//                            override fun handleMessage(msg: Message) {
//                                Toast.makeText(appContext, "配置更新，请重新登录", Toast.LENGTH_SHORT).show()
//                                CommonUtils.restartApp(appContext)
//                            }
//                        }.sendEmptyMessageDelayed(1, 1000)
                    }
                }
                Constants.MqttConfig.Topic.CHECK_MACHINE.plus(subId) -> {
                    // 整改信息
                    EventBus.getDefault().post(CheckMachineEvent())
                }
                Constants.MqttConfig.Topic.NOTICE_MSG.plus(subId) -> {
                    // 通知信息
                    EventBus.getDefault().post(NoticeMsgEvent())
                }
                Constants.MqttConfig.Topic.BANNER_IMG -> {
                    // 轮播图/企业文化图更新
                    EventBus.getDefault().post(BannerEvent())
                }
                Constants.MqttConfig.Topic.LAB_INFO.plus(subId) -> {
                    // 实验室信息更新
                    EventBus.getDefault().post(LabInfoEvent(message = message))
                }
                Constants.MqttConfig.Topic.SYS_IMG -> {
                    // 实验室图片更新
                    EventBus.getDefault().post(LabInfoEvent())
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun updateEnvConfig(message: String): Boolean {
        val entity = mGson.fromJson(message, EnvEntity::class.java)
        val data = entity.data ?: return false

        // 服务器新环境
        if (data.baseUrl.isNullOrEmpty()) return false

        // 安全准入检测新环境
        if (data.signinBaseUrl.isNullOrEmpty()) return false

        // MQTT新环境
        if (data.mqttUrl.isNullOrEmpty()) return false

        // MQTT账号
        if (data.mqttName.isNullOrEmpty()) return false

        // MQTT密码
        if (data.mqttPwd.isNullOrEmpty()) return false

        val config = EnvSp.loadEnv(appContext)
        val settingPwd = config?.settingPwd ?: Constants.Mock.SETTING_PWD
        val apiVersion = config?.apiVersion ?: Constants.NetExtra.API_VERSION

        val newEnv = if (!data.baseUrl.endsWith("/")) "${data.baseUrl}/" else data.baseUrl
        val newSafetyEnv =
            if (!data.signinBaseUrl.endsWith("/")) "${data.signinBaseUrl}/" else data.signinBaseUrl
        val envConfig = EnvConfig(newEnv, newSafetyEnv, data.mqttUrl, data.mqttName, data.mqttPwd, settingPwd, apiVersion)
        EnvSp.saveEnv(appContext, envConfig)

        return true
    }

    private fun handleWarning() {
        val disposable = ApiRepository.warnList(subId)
            .subscribe({ data ->
                EventBus.getDefault().post(WarningEvent(data))
            }, { throwable ->
                if (Constants.Mock.MQTT_TEST) {
                    EventBus.getDefault().post(WarningEvent(DummyData.dummyBulletinBoardWaring()))
                }
                RcLog.info("[MQTT] query warning list error. ${throwable.message}")
                throwable.printStackTrace()
            })
        CompositeDisposable().apply {
            if (!isDisposed) add(disposable)
        }
    }

    private val mDummyHandler = Handler()
    private val mDummySendRunnable = object : Runnable {
        override fun run() {
            val message = mGson.toJson(DummyData.dummyBulletinBoardEntity())
            mMqttWorker.publish(Constants.MqttConfig.Topic.BULLETIN_BOARD.plus(subId), message, 0)
            mMqttWorker.publish(Constants.MqttConfig.Topic.WARNING.plus(subId), message, 0)
            val emergencyMessage = mGson.toJson(DummyData.dummyEmergencyEntity())
            mMqttWorker.publish(Constants.MqttConfig.Topic.EMERGENCY.plus(subId), emergencyMessage, 0)
            mHandler.postDelayed(this, 3000)
        }
    }

    // static + 弱引用  防止内存泄露
    class WeakReferenceHandler(obj: MqttManager) : Handler(Looper.getMainLooper()) {
        private val mRef: WeakReference<MqttManager> = WeakReference(obj)

        override fun handleMessage(msg: Message) {
            mRef.get()?.run {
                Toast.makeText(appContext, "配置更新，请重新登录", Toast.LENGTH_SHORT).show()
                CommonUtils.restartApp(appContext)
            }
        }
    }

}