class PahoIoTClient:
    """
    Responsible for connecting to AWS IoT. Handles all connection lifecycle events and attempts to reconnect whenever possible if disconnected. Data is sent to IoT via the method .send_telemetry(TelemetryMessage).
    """

    MQTT_QOS_RETRY_INTERVAL_S = 60
    KEEP_ALIVE_TIMEOUT_S = 2 * 60

    config: IotClientConfig
    mqtt_client: Client()
    event_listener: IoTEventListener
    topic_subscriptions: list

    important_paho_messages = [
        "Sending PUBLISH (d1", "Connection failed, retrying"
    ]

    def __init__(self, config: IotClientConfig,
                 event_listener: IoTEventListener):
        self.set_config(config=config)
        self.event_listener = event_listener
        self.mqtt_client = Client(config.client_id)
        self.mqtt_client.message_retry_set(self.MQTT_QOS_RETRY_INTERVAL_S)
        # self.mqtt_client.tls_set(ca_certs = config.ca_path, certfile = config.certificate_path, keyfile = config.private_key_path)
        self.mqtt_client.username_pw_set(username=config.username,
                                         password=config.password)
        self.mqtt_client.on_connect = self.on_connected
        self.mqtt_client.on_disconnect = self.on_disconnected
        self.mqtt_client.on_publish = self.on_message_published
        self.mqtt_client.on_message = self.on_message_received

    def set_config(self, config: IotClientConfig):
        self.config = config

    def is_connected(self) -> bool:
        return self.mqtt_client.is_connected()

    def connect(self):
        self.mqtt_client.connect_async(host=self.config.endpoint,
                                       port=self.config.port,
                                       keepalive=self.KEEP_ALIVE_TIMEOUT_S)
        self.mqtt_client.loop_start()

    def disconnect(self):
        self.mqtt_client.disconnect()
        self.mqtt_client.loop_stop()

    # async def reconnect(self, delay_in_s: int = 5):
    #     await asyncio.sleep(delay_in_s)
    #     self.disconnect()
    #     await asyncio.sleep(delay_in_s)
    #     self.connect()

    def publish(self,
                topic: str,
                data: str,
                qos: int = 1,
                reconnect_after_fail: bool = False) -> (int, int):
        message_info = self.mqtt_client.publish(topic=topic,
                                                payload=data,
                                                qos=qos)
        if message_info.rc is not MQTT_ERR_SUCCESS:
            pass
        else:
            pass
        return (message_info.rc, message_info.mid)

    def is_response_code_success(self, response_code: int) -> bool:
        return response_code is MQTT_ERR_SUCCESS

    def initialise_subscriptions_list(self):
        self.topic_subscriptions = []

    def subscribe(self, subscriptions: list):
        for subscription in subscriptions:
            if not self.is_topic_subscribed(subscription):
                self.topic_subscriptions.append(subscription)

        self.mqtt_client.subscribe(topic=self.topic_subscriptions)

    def is_topic_subscribed(self, subscription):
        for existing_subscription in self.topic_subscriptions:
            if subscription[0] == existing_subscription[0]:
                return True
        return False

    def on_connected(self, client, userdata, flags, rc):
        self.initialise_subscriptions_list()

        if self.event_listener is not None:
            self.event_listener.on_iot_connected(iot_client=self)

    def on_disconnected(self, client, userdata, rc):
        # Clear the list of subscriptions
        if self.event_listener is not None:
            self.event_listener.on_iot_disconnected(iot_client=self)

    def on_message_published(self, client, userdata, mid):
        if self.event_listener is not None:
            self.event_listener.on_iot_message_published(
                self,
                message_id=mid,
                payload_size=self.payload_sizes.pop("{0}".format(mid), None))

    def on_message_received(self, client, userdata, message):
        if self.event_listener is not None:
            self.event_listener.on_iot_message_received(
                iot_client=self, topic=message.topic, payload=message.payload)