import logging
from threading import Lock
from typing import List

import zmq
from messagebus import MessageBus
from messagehandler import MessageHandlerBuilder
from zeromqthreadpool import WorkerThread

from ._MessageDecoder import MessageDecoder

logger = logging.getLogger("ZeroMqMessageHandler.HandleEventWorkerThread")


class HandleEventWorkerThread(WorkerThread):
    non_reentrant_lock = Lock()
    non_reentrant_context_locks = {}

    def __init__(self,
                 context,
                 worker_address,
                 idle_timeout,
                 identity,
                 message_bus,
                 message_handler_builder,
                 non_reentrant_events):
        # type: (zmq.Context, str, int, str, MessageBus, MessageHandlerBuilder, List[str]) -> None
        super(HandleEventWorkerThread, self).__init__(context, worker_address, idle_timeout, identity)
        self.message_bus = message_bus
        self.message_handler_builder = message_handler_builder
        self.non_reentrant_events = {}
        if non_reentrant_events is not None:
            for subject in non_reentrant_events:
                self.non_reentrant_events[subject] = subject

    def do_work(self, data):
        event = MessageDecoder.parse_event(data)
        if event is None:
            return

        context = event.headers.get("context", "")
        if event.subject in self.non_reentrant_events:
            with self.non_reentrant_lock:
                if context not in self.non_reentrant_context_locks:
                    self.non_reentrant_context_locks[context] = {}
                if event.subject not in self.non_reentrant_context_locks[context]:
                    self.non_reentrant_context_locks[context][event.subject] = Lock()
                acquired = self.non_reentrant_context_locks[context][event.subject].acquire(blocking=False)
                if not acquired:
                    return
        message_handler = None
        try:
            message_handler = self.message_handler_builder.build_message_handler(None, event)
            message_handler.handle_event(self.message_bus, event)
        except:
            logger.exception("Error handling event: {} - {} - {}".format(event.subject,
                                                                         event.event_type,
                                                                         event.data))
        finally:
            if event.subject in self.non_reentrant_events:
                with self.non_reentrant_lock:
                    self.non_reentrant_context_locks[context][event.subject].release()
            if message_handler is not None:
                self.message_handler_builder.destroy_message_handler(message_handler)

    def dispose(self):
        pass
