package com.example.uitugasakhir.mqtt

import android.content.Context
import android.util.Log
import info.mqtt.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.*
import javax.net.ssl.SSLSocketFactory

class MqttManager(private val context: Context) {

    // =====================================================
    // MQTT CONFIG
    // =====================================================
    private val serverUri =
        "ssl://9feefbc57c4f41f6a4fde7c48e2a35ad.s1.eu.hivemq.cloud:8883"

    private val username = "mqttuser2"

    private val password = "Mqtt12345"

    private val clientId =
        "ANDROID_${System.currentTimeMillis()}"

    private val client =
        MqttAndroidClient(context, serverUri, clientId)

    // =====================================================
    // CONNECT
    // =====================================================
    fun connect(
        onMessage: (topic: String, message: String) -> Unit,
        onConnected: (() -> Unit)? = null
    ) {

        val options = MqttConnectOptions().apply {

            userName = username

            password = this@MqttManager.password.toCharArray()

            isAutomaticReconnect = true

            isCleanSession = false

            connectionTimeout = 10

            keepAliveInterval = 30

            socketFactory =
                SSLSocketFactory.getDefault()
        }

        client.setCallback(object : MqttCallback {

            // =================================================
            // MESSAGE
            // =================================================
            override fun messageArrived(
                topic: String?,
                message: MqttMessage?
            ) {

                val msg = message.toString()

                Log.d("MQTT_TOPIC", topic ?: "")
                Log.d("MQTT_MESSAGE", msg)

                onMessage(topic ?: "", msg)
            }

            // =================================================
            // LOST
            // =================================================
            override fun connectionLost(cause: Throwable?) {

                Log.e(
                    "MQTT",
                    "CONNECTION LOST",
                    cause
                )
            }

            override fun deliveryComplete(token: IMqttDeliveryToken?) {}
        })

        // =====================================================
        // CONNECT
        // =====================================================
        client.connect(
            options,
            null,
            object : IMqttActionListener {

                override fun onSuccess(asyncActionToken: IMqttToken?) {

                    Log.d("MQTT", "CONNECTED")

                    subscribeTopics()

                    onConnected?.invoke()
                }

                override fun onFailure(
                    asyncActionToken: IMqttToken?,
                    exception: Throwable?
                ) {

                    Log.e(
                        "MQTT",
                        "CONNECT FAILED",
                        exception
                    )
                }
            }
        )
    }

    // =====================================================
    // SUBSCRIBE
    // =====================================================
    private fun subscribeTopics() {

        val topics = arrayOf(

            "smartplant/alarm",
            "smartplant/pump",
            "smartplant/sensor",
            "smartplant/mode",
            "smartplant/manualPump",
            "smartplant/riwayat"
        )

        topics.forEach { topic ->

            client.subscribe(
                topic,
                1,
                null,
                object : IMqttActionListener {

                    override fun onSuccess(asyncActionToken: IMqttToken?) {

                        Log.d(
                            "MQTT_SUB",
                            "SUCCESS -> $topic"
                        )
                    }

                    override fun onFailure(
                        asyncActionToken: IMqttToken?,
                        exception: Throwable?
                    ) {

                        Log.e(
                            "MQTT_SUB",
                            "FAILED -> $topic",
                            exception
                        )
                    }
                }
            )
        }
    }

    // =====================================================
    // PUBLISH
    // =====================================================
    fun publish(
        topic: String,
        payload: String
    ) {

        try {

            val message =
                MqttMessage(payload.toByteArray())

            message.qos = 1
            message.isRetained = false

            client.publish(topic, message)

            Log.d(
                "MQTT_PUBLISH",
                "$topic -> $payload"
            )

        } catch (e: Exception) {

            Log.e(
                "MQTT",
                "PUBLISH ERROR",
                e
            )
        }
    }

    // =====================================================
    // MODE
    // =====================================================
    fun setMode(mode: String) {

        publish(
            "smartplant/mode",
            mode
        )
    }

    // =====================================================
    // MANUAL PUMP
    // =====================================================
    fun setPump(state: Boolean) {

        val payload = if (state) "ON" else "OFF"

        publish(
            "smartplant/manualPump",
            payload
        )
    }

    fun setModeAuto() {
        publish("smartplant/mode", "Otomatis")
    }

    fun setModeManual() {
        publish("smartplant/mode", "Manual")
    }

    // =====================================================
    // DISCONNECT
    // =====================================================
    fun disconnect() {

        try {

            client.unregisterResources()
            client.close()

        } catch (e: Exception) {

            e.printStackTrace()
        }
    }
}