import logging
import threading
from logging import Logger
from typing import Optional, List, Type

from messageprocessor import MessageProcessorCallback

default_logger = logging.getLogger("MessageProcessor.LoggingProcessorCallback")


class LoggingProcessorCallback(MessageProcessorCallback):
    def __init__(self, logger, ignored_exceptions=None):
        # type: (Logger, Optional[List[Type[Exception]]]) -> None
        self.logger = logger if logger is not None else default_logger
        self.ignored_exceptions = {}
        if ignored_exceptions is not None:
            for ex in ignored_exceptions:
                self.ignored_exceptions[ex] = ex
        self.local_storage = threading.local()

    def process_begun(self, event_name, request_id, data):
        # type: (str, str, bytes) -> None
        self.local_storage.original_thread_name = threading.current_thread().name
        self._set_thread_name(event_name, request_id)
        try:
            converted_data = data.decode(u"utf-8")
        except:
            try:
                converted_data = str(data)
            except:
                converted_data = u"could not convert data: {}. Implementing __str__ might help".format(type(data))

        self.logger.debug(u"Start handling. Data: {}".format(converted_data))

    def input_parsed(self, event_name, request_id, input_obj):
        self._set_thread_name(event_name, request_id)
        self.logger.debug(u"Input correctly parsed")

    def business_called(self, event_name, request_id, input_obj, output_obj):
        pass

    def process_finished(self, event_name, request_id, input_obj, output_obj):
        self.logger.info(u"Successfully processed")
        self._restore_thread_name()

    def parse_exception(self, event_name, request_id, data, exception):
        if type(exception) not in self.ignored_exceptions:
            self.logger.exception(u"Parse exception handling")
        self._restore_thread_name()

    def unhandled_exception(self, event_name, request_id, input_obj, exception):
        if type(exception) not in self.ignored_exceptions:
            self.logger.exception(u"Unhandled exception handling")
        self._restore_thread_name()

    @staticmethod
    def _set_thread_name(event_name, request_id):
        threading.current_thread().name = "MessageProcessorThread${}${}".format(event_name, request_id)

    def _restore_thread_name(self):
        threading.current_thread().name = self.local_storage.original_thread_name
