package cn.org.bjca.trust.push.message.client

import android.os.Handler
import android.os.Looper
import android.os.Message
import cn.org.bjca.trust.push.BuildConfig
import cn.org.bjca.trust.push.common.DeviceHelper
import cn.org.bjca.trust.push.common.LogHelper
import cn.org.bjca.trust.push.common.MessageHelper
import cn.org.bjca.trust.push.common.json.GsonImplHelp
import cn.org.bjca.trust.push.constant.*
import cn.org.bjca.trust.push.db.DbHelper
import cn.org.bjca.trust.push.db.message.SzyxMessage
import cn.org.bjca.trust.push.enums.OsType
import cn.org.bjca.trust.push.enums.PacketType
import cn.org.bjca.trust.push.kit.ImClientInterface
import cn.org.bjca.trust.push.message.msg.*
import org.eclipse.paho.client.mqttv3.*
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

class ImClient : ImClientInterface {
    private lateinit var connectOptions: ImConnectOptions
    private lateinit var mqttArrivedClient: MqttClient
    private lateinit var options: MqttConnectOptions
    private var isConnecting = false
    private var startConnect = false

    override fun imConnectOptions(): ImConnectOptions = connectOptions

    override fun connect(mImConnectOptions: ImConnectOptions) {
        startConnect = true
        connectOptions = mImConnectOptions
        init()
        mConnect()
        startReconnect()
    }

    private fun mConnect() {
        if (isConnect() || isConnecting) return
        isConnecting = true
        Thread {
            try {
                if (!mqttArrivedClient.isConnected) {
                    LogHelper.d("------------开始重连::mqttArrivedClient--------------")
                    mqttArrivedClient.connect(options)
                }
            } catch (e: MqttSecurityException) {
                LogHelper.e("--------", e)
                connectOptions.callback?.connectError(ERROR_CONNECTED)
            } catch (e: MqttException) {
                LogHelper.e("--------", e)
                connectOptions.callback?.connectError(ERROR_CONNECTED)
            }
            isConnecting = false
        }.start()
    }

    private fun init() {
        if (!::mqttArrivedClient.isInitialized) {
            mqttArrivedClient = MqttClient(
                "tcp://${connectOptions.host}:${connectOptions.port}",
                connectOptions.clientId,
                MemoryPersistence()
            )
            options = MqttConnectOptions()
            options.isCleanSession = true
            options.userName = connectOptions.clientId
            options.password = "xuqinmin".toCharArray()
            options.connectionTimeout = connectOptions.connectTimeOutSecond
            options.keepAliveInterval = connectOptions.pingInterval
            options.isAutomaticReconnect = true
            mqttArrivedClient.setCallback(object : MqttCallbackExtended {
                override fun connectionLost(cause: Throwable?) {
                    reConnect()
                }

                override fun messageArrived(topic: String?, message: MqttMessage) {
                    arrived(message.payload)
                }

                override fun deliveryComplete(token: IMqttDeliveryToken?) {
                }

                override fun connectComplete(reconnect: Boolean, serverURI: String) {
                    mqttArrivedClient.subscribe(connectOptions.clientId, 2)
                    sendConnectMessage()
                }
            })
        }
    }

    private fun arrived(payload: ByteArray) {
        val str = String(payload, Charsets.UTF_8)
        val msg =
            GsonImplHelp.get().toObject(str, BaseMessage::class.java)

        when (msg.getPacketType()) {
            PacketType.CONNACK -> {
                connectOptions.callback?.connected()
            }
            PacketType.SEND -> {
                LogHelper.d("------新消息------\n${str}")
                val sendMessage = GsonImplHelp.get()
                    .toObject(String(payload, Charsets.UTF_8), SendMessage::class.java)
                if (sendMessage.messageId.isNullOrEmpty()) return
                sendMessage.messageId?.let { ackMessage(it) }
                val his = DbHelper.getDataBase().messageDao().getMessage(sendMessage.messageId!!)
                if (his.isNotEmpty()) return
                // 存表
                if (sendMessage.messageType < 100)
                    DbHelper.getDataBase().messageDao().insertAll(
                        SzyxMessage(
                            sendMessage.messageId,
                            sendMessage.timestamp,
                            sendMessage.fromClientId,
                            sendMessage.toClientId,
                            sendMessage.target,
                            sendMessage.messageType,
                            sendMessage.message,
                            sendMessage.tenantNo,
                            1
                        )
                    )
                // 通知app新消息到达
                MessageHelper.parseMessage(sendMessage)?.also {
                    connectOptions.callback?.messageArrived(it)
                }
            }
            PacketType.SENDACK -> {
                val ackSendMessage = GsonImplHelp.get()
                    .toObject(String(payload, Charsets.UTF_8), AckSendMessage::class.java)
                ackSendMessage.messageId?.let {
                    MessageHelper.changeStatus(it, true)
                        ?.let { it1 -> connectOptions.callback?.sendComplete(it1) }
                }

            }
            PacketType.PANG -> {
                LogHelper.d("--------------------PANG----------------------")
            }
            else -> {

            }
        }


    }

    override fun reConnect() {
        if (!startConnect) return
        init()
        mConnect()
    }

    override fun disConnect() {
        startConnect = false
        if (::mqttArrivedClient.isInitialized && mqttArrivedClient.isConnected) mqttArrivedClient.disconnect()
        isConnecting = false
    }


    private lateinit var scheduler: ScheduledExecutorService
    private lateinit var pingScheduler: ScheduledExecutorService

    // ping和断线重连
    private fun startReconnect() {
        if (!::scheduler.isInitialized) {
            scheduler = Executors.newSingleThreadScheduledExecutor()
            scheduler.scheduleAtFixedRate({
                reConnect()
            }, 2, 3, TimeUnit.SECONDS)
        }

        if (!::pingScheduler.isInitialized) {
            pingScheduler = Executors.newSingleThreadScheduledExecutor()
            pingScheduler.scheduleAtFixedRate({
                ping()
            }, 3, connectOptions.pingInterval.toLong(), TimeUnit.SECONDS)
        }

    }

    override fun isConnect(): Boolean {
        return ::mqttArrivedClient.isInitialized && mqttArrivedClient.isConnected
    }

    override fun isConnecting(): Boolean {
        return isConnecting
    }

    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val msgId = msg.obj as String
            MessageHelper.changeStatus(msgId, false)?.let {
                connectOptions.callback?.sendError(ERROR_MSG_SEND_1, it)
            }
        }

    }

    override fun sendMessage(msg: SendMessage) {
        if (!::connectOptions.isInitialized) {
            MessageHelper.parseMessage(msg)?.let {
                connectOptions.callback?.sendError(
                    ERROR_MSG_SEND_4, it
                )
            }
            return
        }
        msg.fromClientId = connectOptions.clientId
        msg.target = connectOptions.clientId

        if (msg.messageType < 100)
            DbHelper.getDataBase().messageDao().insertAll(
                SzyxMessage(
                    msg.messageId,
                    msg.timestamp,
                    msg.fromClientId,
                    msg.toClientId,
                    msg.target,
                    msg.messageType,
                    msg.message,
                    msg.tenantNo,
                    0
                )
            )
        if (!isConnect()) {
            msg.messageId?.let { MessageHelper.changeStatus(it, false) }
            MessageHelper.parseMessage(msg)?.let {
                connectOptions.callback?.sendError(
                    ERROR_MSG_SEND_2, it
                )
            }
            return
        }
        try {
            val m = GsonImplHelp.get().toJson(msg)
            LogHelper.d("------app端调用发送消息-----\t${m}")
            mqttArrivedClient.publish(
                "server", m.toByteArray(Charsets.UTF_8), 2, true
            )
            handler.sendMessageDelayed(Message().apply {
                what = 10086
                obj = msg.messageId
            }, 1500)
        } catch (e: MqttException) {
            LogHelper.e("发送消息失败", e)
            e.printStackTrace()
            MessageHelper.parseMessage(msg)
                ?.let { connectOptions.callback?.sendError(ERROR_MSG_SEND_3, it) }
            msg.messageId?.let { MessageHelper.changeStatus(it, false) }
        }
    }

    override fun unregister() {
        if (!isConnect()) {
            disConnect()
            return
        }
        try {
            mqttArrivedClient.publish(
                "server",
                GsonImplHelp.get().toJson(DisconnectMessage()).toByteArray(Charsets.UTF_8),
                2,
                false
            )
            disConnect()
        } catch (e: MqttException) {
            LogHelper.d("发送消息失败", e)
        }
    }


    /**
     * im连接成功，发送上线消息
     */
    private fun sendConnectMessage() {
        if (!isConnect()) {
            return
        }
        try {
            mqttArrivedClient.publish(
                "server", GsonImplHelp.get().toJson(ConnectMessage().apply {
                    osType = OsType.ANDROID
                    osVer = DeviceHelper.getDevice().model
                    sdkVer = BuildConfig.versionName
                    clientId = connectOptions.clientId
                    deviceId = connectOptions.deviceId
                }).toByteArray(Charsets.UTF_8), 2, false
            )
        } catch (e: MqttException) {
            LogHelper.d("注册上线服务失败", e)
        }

    }

    /**
     * ping
     */
    private fun ping() {
        if (!isConnect()) {
            return
        }
        try {
            mqttArrivedClient.publish(
                "server",
                GsonImplHelp.get().toJson(PingMessage().apply {
                    fromClientId = connectOptions.clientId
                }).toByteArray(Charsets.UTF_8),
                2,
                false
            )
        } catch (e: MqttException) {
            LogHelper.d("发送ping消息失败", e)
        }

    }

    /**
     * ack
     */
    private fun ackMessage(msgId: String) {
        if (!isConnect()) {
            return
        }
        try {
            mqttArrivedClient.publish(
                "server", GsonImplHelp.get().toJson(AckSendMessage().apply {
                    messageId = msgId
                }).toByteArray(Charsets.UTF_8), 2, false
            )
        } catch (e: MqttException) {
            LogHelper.d("回复服务器消息失败", e)
        }

    }
}