import json
from SKFClient.core.jobs.base_thread import BaseThread
from SKFClient.core.utils.log import log
import time
from SKFClient.lorasync import lorasync


class AwsShadowDeltaGetJob(BaseThread):
    def __init__(self):
        BaseThread.__init__(self)
        self.first_time_init = False

    def gateway_ping_request(self):
        json_message = {
            "productId": self.app_context.config.product_id,
            "deviceId": self.app_context.config.device_id,
            "retries": self.app_context.myrina_retry
        }
        out_message = json.dumps(json_message)
        self.app_context.aws_mqtt.publish(self.app_context.config.aws_mqtt_config.gateway_request_topic, out_message,
                                          qos=0)

    def gateway_keys_request(self):
        self.app_context.aws_mqtt.publish(self.app_context.config.aws_mqtt_config.key_request_topic, payload="{}", qos=0)

    def on_shadow_get(self, message):
        log.debug("on_shadow_get message: {}".format(message))
        '''
        const payload = JSON.parse(msg.payload)
        const desired = payload.state.desired ? payload.state.desired.devicesUpdatedAt : null
        const current = payload.state.reported ? payload.state.reported.devicesUpdatedAt : null
        const retries = flow.get('myrina-retry') || 0

        if (desired !== current && retries < 5) {
         // Update needed
         return {
            devicesUpdatedAt: desired
         }
        } else {
         return null
        }
        '''
        json_message = json.loads(message)
        desired = None
        current = None
        if "desired" in json_message["state"]:
            desired = json_message["state"]["desired"]["devicesUpdatedAt"]
        if "reported" in json_message["state"]:
            current = json_message["state"]["reported"]["devicesUpdatedAt"]
        if desired != current:
            self.gateway_ping_request()
            self.gateway_keys_request()

    def on_shadow_delta(self, message):
        log.debug("on_shadow_delta message: {}".format(message))
        '''
        { "topic": "$aws/things/myrinagw-VT52JN88/shadow/update/delta",
        "payload": "{"version":28,"timestamp":1645194058,
        "state":{"devicesUpdatedAt":1645194058227},
        "metadata":{"devicesUpdatedAt":{"timestamp":1645194058}}}",
        "qos": 1, "_msgid": "58107b28.a7ef84" }

        return JSON.parse(msg.payload).state
        '''
        self.gateway_ping_request()
        self.gateway_keys_request()

    def on_keys_response(self, message):
        log.debug("on_keys_response message: {}".format(message))
        '''

        {"keys":{"008000FF00000001":["0FAC4724415FA498","6277EF7F8295E014E83B5C984F73ACF4"]}}

        const keys = JSON.parse(msg.payload).keys
        const args = []
        Object.keys(keys).map(function(deviceId) {
            args.push(deviceId + ' ' + keys[deviceId].join(' '))
        })

        return {
            payload: args.join(' ')
        }


        { "payload": "008000FF00000001 0FAC4724415FA498 6277EF7F8295E014E83B5C984F73ACF4", "_msgid": "ab51561c.54aea8" }
        '''

        json_message = json.loads(message)
        new_keys = {}
        try:
            for dev_eui, dev_info in json_message["keys"].items():
                dev_eui = str(dev_eui.lower().replace('-', ''))
                new_keys[dev_eui] = {
                    'deveui': str(dev_eui).lower(),
                    'appeui': str(dev_info[0]).lower(),
                    'appkey': str(dev_info[1]).lower(),
                }
        except Exception as error:
            log.error('Failed to parse new key {}'.format(str(error)))
            log.debug(error, exc_info=True)
            return

        log.info("New key {}".format(new_keys))

        try:
            lorasync(new_keys)
        except:
            log.error("Exception", exc_info=True)
            self.app_context.myrina_retry = self.app_context.myrina_retry + 1
            return
        self.app_context.myrina_retry = 0

        epoch_time = int(time.time())
        json_message = {
            "state": {
                "desired": {
                    "devicesUpdatedAt": epoch_time
                },
                "reported": {
                    "devicesUpdatedAt": epoch_time
                }
            }
        }
        out_message = json.dumps(json_message)

        self.app_context.aws_mqtt.publish(self.app_context.config.aws_mqtt_config.update_shadow_topic, out_message,
                                          qos=0)

    def incoming_shadow_delta_msg(self, client, userdata, message):
        log.info("Received shadow delta message '" + str(message.payload) + "' on topic '"
                 + message.topic + "' with QoS " + str(message.qos))
        self.incoming_msg('shadow_delta', message.payload)

    def incoming_shadow_get_msg(self, client, userdata, message):
        log.debug("Received shadow get message '" + str(message.payload) + "' on topic '"
                 + message.topic + "' with QoS " + str(message.qos))
        self.incoming_msg('shadow_get', message.payload)

    def incoming_keys_response_msg(self, client, userdata, message):
        log.info("Received keys response message '" + str(message.payload) + "' on topic '"
                 + message.topic + "' with QoS " + str(message.qos))
        self.incoming_msg('keys_response', message.payload)

    def on_timer(self, msg):
        log.debug("on_timer")
        self.app_context.aws_mqtt.publish(self.app_context.config.aws_mqtt_config.get_shadow_topic, payload="{}", qos=0)
        if not self.first_time_init:
            self.app_context.aws_mqtt.publish(self.app_context.config.aws_mqtt_config.key_request_topic, payload="{}",
                                              qos=0)
            self.first_time_init = True


    def init(self, app_context):
        BaseThread.init(self, app_context)

        app_context.aws_mqtt.subscribe(app_context.config.aws_mqtt_config.on_shadow_get_topic, 0,
                                       self.incoming_shadow_get_msg)
        app_context.aws_mqtt.subscribe(app_context.config.aws_mqtt_config.on_shadow_delta_topic, 0,
                                       self.incoming_shadow_delta_msg)
        # app_context.aws_mqtt.subscribe(app_context.config.aws_mqtt_config.key_response_topic, 0,
        #                               self.incoming_keys_response_msg)

        self.set_timeout(60)  # every 1 minutes
