"""
Custom wrapper for message broker
"""
import os
import time
import json
import uuid
import pika
from loguru import logger

class MQAdapter:
    """MessageQueue wrapper containing functionality for interacting with the
    message broker via AMQP
    """

    def __init__(self, channel, method, properties):
        self.__channel = channel
        self.__method = method
        self.__properties = properties

        self.__rpc_timeout = 5
        self.__rpc_responses = {}

        # Setup new connection to RabbitMQ for RPC
        self.__rpc_connection = pika.BlockingConnection(
            pika.ConnectionParameters(
                host=os.environ["MQ_HOSTNAME"],
                port=os.environ["MQ_PORT"],
                credentials=pika.PlainCredentials(
                    os.environ["MQ_USERNAME"],
                    os.environ["MQ_PASSWORD"]
                )
            )
        )

        # Get RPC channel
        self.__rpc_channel = self.__rpc_connection.channel()

        # Initiate reply queuee
        self.__rpc_queue = f"{uuid.uuid4()}-service.rpc.queue"

        self.__rpc_channel.queue_declare(
            queue=self.__rpc_queue,
            exclusive=False,
            auto_delete=True
        )

    def publish(self, exchange: str, routing_key: str, payload: dict):
        self.__channel.basic_publish(
            exchange=exchange,
            routing_key=routing_key,
            body=json.dumps(payload)
        )

    def rpc(self, routing_key: str, payload: dict):
        # Configure correlation ID for response routing
        correlation_id = str(uuid.uuid4())

        # Add RPC fields to payload
        self.__rpc_responses[correlation_id] = None

        # Make RPC call
        self.__rpc_channel.basic_publish(
            exchange="rpc",
            routing_key=routing_key,
            body=json.dumps(payload),
            properties=pika.BasicProperties(
                content_type='application/json',
                reply_to=self.__rpc_queue,
                correlation_id=correlation_id
            )
        )

        # Await a response to our request for a maximium of rpc_timeout
        response = None
        start = time.time()
        for _method, _properties, body in self.__rpc_channel.consume(
            self.__rpc_queue,
            auto_ack=True,
            inactivity_timeout=0.1
        ):
            # If the message body is empty check again for a response
            if body:
                # Parse RPC response message
                message = json.loads(body)

                # If a response to our request has been received we return it to the caller
                if (
                    "correlation-id" in message and
                    message["correlation-id"] == correlation_id
                ):
                    response = message
                    break

            # If we have exceeded our timeout period, return None
            if (time.time() - start) > self.__rpc_timeout:
                break

        return response

    def cleanup(self):
        # Cleanup RPC queue
        self.__rpc_channel.queue_delete(queue=self.__rpc_queue)

        # Cleanup RPC connection
        self.__rpc_connection.close()
