from __future__ import annotations
import time
from messaging.MessageManager import MessageManager

import logging
LOGGER = logging.getLogger(__name__)

class ReconnectingMessageManager:
    '''This Class can be used to access RabbitMQ. It handles connection errors by trying to reconnect every second'''
    _instance = None

    _message_manager: MessageManager = None
    _consuming = False
    _consumption_parameters = None
    _sending_parameters = None

    @staticmethod 
    def getInstance() -> ReconnectingMessageManager:
        ''' Static access method. '''
        if ReconnectingMessageManager._instance == None:
            ReconnectingMessageManager._instance = ReconnectingMessageManager()
        return ReconnectingMessageManager._instance

    def __init__(self):
        '''Do not use the constructor as it is a Singleton!'''
        if ReconnectingMessageManager._instance != None:
            raise Exception("This class is a singleton!")
        
        ReconnectingMessageManager._instance = self
        self._init_message_manager()
    
    def _init_message_manager(self):
        self._message_manager = MessageManager()
        self._message_manager.connect(self._pika_error_callback)
        time.sleep(1)
       
    def _pika_error_callback(self, error):
        # restart receiver
        self._message_manager.disconnect()
        self._init_message_manager()

        if self._consuming:
            self._restart_consuming()

        if self._sending_parameters != None:
            self._reinit_sending()

    def _restart_consuming(self):
        if self._consumption_parameters != None:
            self.start_consuming(self._consumption_parameters['exchange_name'],
                            self._consumption_parameters['exchange_type'],
                            self._consumption_parameters['queue_name'],
                            self._consumption_parameters['auto_ack'],
                            self._consumption_parameters['message_received_callback'])

    def _reinit_sending(self):
        if self._sending_parameters != None:
            self.create_message_destination(self._sending_parameters['exchange_name'],
                                            self._sending_parameters['exchange_type'])

    def start_consuming(self, exchange_name, exchange_type, queue_name, auto_ack, message_received_callback):
        '''Creates exchange and queue and starts to listen to new messages'''
        self._consumption_parameters = {'exchange_name': exchange_name, 
                                        'exchange_type': exchange_type,
                                        'queue_name': queue_name, 
                                        'auto_ack': auto_ack, 
                                        'message_received_callback': message_received_callback}
        self._consuming = True

        try:
            self._message_manager.create_exchange_with_queue(exchange_name, exchange_type, queue_name, self._exchange_created_callback)
        except:
            LOGGER.error("Error while creating exchange and queue")

    def _exchange_created_callback(self):
        LOGGER.info("Exchange and queue set up")
        self._message_manager.start_consuming(self._consumption_parameters['queue_name'], 
            self._consumption_parameters['auto_ack'], 
            self._consumption_parameters['message_received_callback'])

    def create_message_destination(self, exchange_name, exchange_type):
        '''Creates the exchange for sending messages'''
        self._sending_parameters = {'exchange_name': exchange_name,
                                    'exchange_type': exchange_type}
        try:
            self._message_manager.create_exchange(exchange_name, exchange_type)
        except:
            LOGGER.error("Error while creating exchange")    

    def send_message(self, exchange_name, routing_key, message):
        '''Sends a message to the exchange'''
        try:
            self._message_manager.send_message(exchange_name, routing_key, message)
        except:
            LOGGER.error("Error while sending message")
