import logging

from kombu import Connection, Exchange, Producer
from xivo_bus import Marshaler
from xivo_bus.resources.ipbx.event import ReloadIpbxEvent

logger = logging.getLogger(__name__)


class BusPublisher(object):
    DELAYED_MESSAGES = ['config.ipbx.reload']

    @classmethod
    def from_config(cls, config):
        url = 'amqp://{username}:{password}@{host}:{port}//'.format(**config['bus'])
        bus_config = config['bus']
        uuid = config['uuid']
        return cls(url, bus_config, uuid)

    def __init__(self, url, bus_config, uuid):
        self.url = url
        self.exchange_name = bus_config['exchange_name']
        self.exchange_type = bus_config['exchange_type']
        self.exchange_args = bus_config['exchange_args']
        self.reload_delay = bus_config['reload_delay']
        self.uuid = uuid
        self.messages = []
        self.reload_message = None

    def send_bus_event(self, message, routing_key):
        if isinstance(message, ReloadIpbxEvent):
            self.add_optimized_reload_message(message)
        else:
            self.messages.append((message, routing_key))

    def add_optimized_reload_message(self, message):
        if self.reload_message:
            optimized_commands = list(set(self.reload_message.commands + message.commands))
            self.reload_message.commands = optimized_commands
        else:
            self.reload_message = message

    def flush(self):
        exchange = Exchange(self.exchange_name,
                            self.exchange_type,
                            arguments=self.exchange_args)
        marshaler = Marshaler(self.uuid)
        with Connection(self.url) as connection:
            producer = Producer(connection, exchange=exchange, auto_declare=True)
            publish = connection.ensure(producer, producer.publish,
                                        errback=self.publish_error, max_retries=3,
                                        interval_start=1)
            self.send_messages(marshaler, publish)

    def send_messages(self, marshaler, publish):
        all_messages = self.messages
        if self.reload_message:
            all_messages.append((self.reload_message, self.reload_message.routing_key))
        for event, routing_key in self.messages:
            message = marshaler.marshal_message(event)
            headers = self.delay_message(routing_key)
            publish(message, content_type=marshaler.content_type, routing_key=routing_key, headers=headers)

    def delay_message(self, routing_key):
        headers = None
        if routing_key in self.DELAYED_MESSAGES:
            headers = {'x-delay': int(self.reload_delay)}
        return headers

    def publish_error(self, exc, interval):
        logger.error('Error: %s', exc, exc_info=1)
        logger.info('Retry in %s seconds...', interval)

    def rollback(self):
        self.messages = []
        self.reload_message = None
