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.db.DbHelper
import cn.org.bjca.trust.push.db.message.SzyxMessage
import cn.org.bjca.trust.push.enums.ConnAckReturnCode
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 connect(mImConnectOptions: ImConnectOptions) {
        startConnect = true
        connectOptions = mImConnectOptions
        init()
        mConnect()
    }

    private fun mConnect() {
        if (isConnect() || isConnecting) return
        isConnecting = true
        Thread {
            try {
                if (!mqttArrivedClient.isConnected) {
                    LogHelper.d("------------开始重连::mqttArrivedClient--------------")
                    mqttArrivedClient.connect(options)
                }
            } catch (e: Exception) {
                connectOptions.callback?.connectError(ConnAckReturnCode.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 = "xuqinmin1022".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?) {
                    LogHelper.e(
                        "----------IM::deliveryComplete-------------"
                    )
                }

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

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

        when (msg.getPacketType()) {
            PacketType.CONNACK -> {
                connectOptions.callback?.connected()
            }
            PacketType.SEND -> {
                val sendMessage = GsonImplHelp.get()
                    .toObject(String(payload, Charsets.UTF_8), SendMessage::class.java)
                // 存表
                DbHelper.getDataBase().messageDao().insertAll(
                    SzyxMessage(
                        sendMessage.messageId,
                        sendMessage.timestamp,
                        sendMessage.fromClientId,
                        sendMessage.toClientId,
                        sendMessage.target,
                        sendMessage.messageType,
                        sendMessage.message,
                        sendMessage.tenantNo,
                        1
                    )
                )
                // 回复服务端收到
                sendMessage.messageId?.let { ackMessage(it) }
                // 通知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 -> {

            }
            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和断线重连
    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("1001", it)
            }
        }

    }

    override fun sendMessage(msg: SendMessage) {
        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("1002", it) }
            return
        }
        try {
            mqttArrivedClient.publish(
                "server", GsonImplHelp.get().toJson(msg).toByteArray(Charsets.UTF_8), 2, true
            )
            handler.sendMessageDelayed(android.os.Message().apply {
                what = 10086
                obj = msg.messageId
            }, 1500)
        } catch (e: MqttException) {
            LogHelper.e("发送消息失败", e)
            e.printStackTrace()
            MessageHelper.parseMessage(msg)?.let { connectOptions.callback?.sendError("1003", 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("ping失败", e)
        }

    }

    /**
     * ping
     */
    private fun ping() {
        if (!isConnect()) {
            return
        }
        try {
            mqttArrivedClient.publish(
                "server",
                GsonImplHelp.get().toJson(PingMessage()).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("ping失败", e)
        }

    }
}