import pytest

from messagebus import Event, MessageBus
from messageprocessor import \
    MessageProcessorMessageHandler, \
    MessageProcessor, \
    MessageProcessorExecutorFactory, \
    MessageProcessorExecutor, \
    ApiRouter, \
    ApiRequest


class TestMessageProcessorEventHandlerTestCase(object):
    def test_whenNoEventProcessorNorApiRouterIsPresent_EventNotMappedIsRaised(self):
        message_bus = DummyMessageBus()

        message_handler = a_message_handler().build()
        event = Event(u"subject", u"type", b"")

        with pytest.raises(MessageProcessorMessageHandler.EventNotMapped):
            message_handler.handle_event(message_bus, event)

    def test_whenEventIsNotOnProcessorsNorOnRouters_EventNotMappedIsRaised(self):
        message_bus = DummyMessageBus()
        message_handler = a_message_handler().with_event_processors({}).with_api_routers({}).build()

        event = Event(u"subject", u"type", b"")
        with pytest.raises(MessageProcessorMessageHandler.EventNotMapped):
            message_handler.handle_event(message_bus, event)

    def test_whenSubjectIsMappedToAMessageProcessor_theExecutorCreatedByTheFactoryIsExecuted(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactoryStub()
        event_processors = {
            u"subject": DummyMessageProcessor()
        }
        data = b"asdf"
        event = Event(u"subject", u"type", data)

        event_handler = a_message_handler()\
            .with_event_processors(event_processors)\
            .with_executor_factory(executor_factory)\
            .build()
        event_handler.handle_event(message_bus, event)

        executor_spy = executor_factory.created_executor  # type: MessageProcessorExecutorSpy
        assert executor_spy.execute_processor_called
        assert message_bus == executor_spy.message_bus_passed
        assert event == executor_spy.event_passed
        assert executor_spy.message_passed is None
        assert data == executor_spy.data_passed

    def test_whenSubjectAndTypeAreMappedToAMessageProcessor_theExecutorCreatedByTheFactoryIsExecuted(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactoryStub()
        event_processors = {
            u"subject_type": DummyMessageProcessor()
        }
        data = b"asdf"
        event = Event(u"subject", u"type", data)

        event_handler = a_message_handler()\
            .with_event_processors(event_processors)\
            .with_executor_factory(executor_factory)\
            .build()
        event_handler.handle_event(message_bus, event)

        executor_spy = executor_factory.created_executor  # type: MessageProcessorExecutorSpy
        assert executor_spy.execute_processor_called
        assert message_bus == executor_spy.message_bus_passed
        assert event == executor_spy.event_passed
        assert executor_spy.message_passed is None
        assert data == executor_spy.data_passed

    def test_theSubjectAndTypeHasPreferenceOverOnlySubject(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactorySpy()
        subject_type_message_processor = DummyMessageProcessor()
        event_processors = {
            u"subject_type": subject_type_message_processor,
            u"subject": DummyMessageProcessor()
        }
        data = b"asdf"
        event = Event(u"subject", u"type", data)

        event_handler = a_message_handler() \
            .with_event_processors(event_processors) \
            .with_executor_factory(executor_factory) \
            .build()
        event_handler.handle_event(message_bus, event)

        assert executor_factory.build_executor_called
        assert executor_factory.message_processor_passed == subject_type_message_processor

    def test_theEventProcessorHasPriorityOverApiRouters(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactorySpy()
        subject_message_processor = DummyMessageProcessor()
        event_processors = {
            u"subject": subject_message_processor
        }
        api_routers = {
            u"subject": DummyApiRouter()
        }
        data = b"asdf"
        event = Event(u"subject", "type", data)

        event_handler = a_message_handler() \
            .with_event_processors(event_processors) \
            .with_api_routers(api_routers) \
            .with_executor_factory(executor_factory) \
            .build()
        event_handler.handle_event(message_bus, event)

        assert executor_factory.build_executor_called
        assert executor_factory.message_processor_passed == subject_message_processor

    def test_whenEventIsMappedToAnApiRouterAndAnApiRequestIsOnTheData_ThisApiRequestIsPassedToTheExecutor(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactoryStub()
        api_routers = {
            u"subject": ApiRouterStub(DummyMessageProcessor())
        }
        api_request = ApiRequest(u"POST", "/", {}, {}, {}, b"")
        event = Event(u"subject", "", api_request)

        event_handler = a_message_handler() \
            .with_api_routers(api_routers) \
            .with_executor_factory(executor_factory) \
            .build()
        event_handler.handle_event(message_bus, event)

        executor_spy = executor_factory.created_executor  # type: MessageProcessorExecutorSpy
        assert executor_spy.execute_processor_called
        assert message_bus == executor_spy.message_bus_passed
        assert event == executor_spy.event_passed
        assert executor_spy.message_passed is None
        assert api_request == executor_spy.data_passed

    def test_whenEventIsMappedToAnApiRouter_TheApiRequestJsonIsCorrectlyMapped(self):
        message_bus = DummyMessageBus()
        executor_factory = MessageProcessorExecutorFactoryStub()
        api_routers = {
            "subject": ApiRouterStub(DummyMessageProcessor())
        }
        api_request_bytes = u"""{
            "api_path": "api_path/{path_param1}/path_c/{path_param2}",
            "headers": {
                "header1": "header1_value",
                "header2": "header2_value"
            },
            "query": {
                "param1": "param1_value",
                "param2": "param2_value",
                "path_param1": "path_param1_value",
                "path_param2": "path_param2_value"
            },
            "body": "asdfasdf"
        }""".encode("utf-8")
        event = Event("subject", "", api_request_bytes)

        event_handler = a_message_handler() \
            .with_api_routers(api_routers) \
            .with_executor_factory(executor_factory) \
            .build()
        event_handler.handle_event(message_bus, event)

        executor_spy = executor_factory.created_executor  # type: MessageProcessorExecutorSpy
        api_request = executor_spy.data_passed  # type: ApiRequest
        assert u"api_path/{path_param1}/path_c/{path_param2}" == api_request.api_path
        assert 2 == len(api_request.headers)
        assert u"header1_value" == api_request.headers[u"header1"]
        assert u"header2_value" == api_request.headers[u"header2"]
        assert 2 == len(api_request.query_parameters)
        assert u"param1_value" == api_request.query_parameters[u"param1"]
        assert u"param2_value" == api_request.query_parameters[u"param2"]
        assert 2 == len(api_request.path_parameters)
        assert u"path_param1_value" == api_request.path_parameters[u"path_param1"]
        assert u"path_param2_value" == api_request.path_parameters[u"path_param2"]
        assert u"asdfasdf" == api_request.body


def a_message_handler():
    return MessageProcessorMessageHandlerBuilder()


class ApiRouterStub(ApiRouter):
    def __init__(self, message_processor):
        self.message_processor = message_processor

    def get_message_processor(self, api_request):
        return self.message_processor


class MessageProcessorMessageHandlerBuilder(object):
    def __init__(self):
        self.event_processors = None
        self.message_processors = None
        self.api_routers = None
        self.executor_factory = None

    def with_event_processors(self, event_processors):
        self.event_processors = event_processors
        return self

    def with_message_processors(self, message_processors):
        self.message_processors = message_processors
        return self

    def with_api_routers(self, api_routers):
        self.api_routers = api_routers
        return self

    def with_executor_factory(self, executor_factory):
        self.executor_factory = executor_factory
        return self

    def build(self):
        return MessageProcessorMessageHandler(
            self.event_processors,
            self.message_processors,
            self.api_routers,
            self.executor_factory)


class DummyApiRouter(ApiRouter):
    def get_message_processor(self, api_request):
        return DummyMessageProcessor()


class MessageProcessorExecutorFactoryStub(MessageProcessorExecutorFactory):
    def __init__(self):
        self.created_executor = None

    def build_executor(self, message_processor):
        self.created_executor = MessageProcessorExecutorSpy()
        return self.created_executor


class MessageProcessorExecutorFactorySpy(MessageProcessorExecutorFactory):
    def __init__(self):
        self.created_executor = None
        self.build_executor_called = False
        self.message_processor_passed = None

    def build_executor(self, message_processor):
        self.build_executor_called = True
        self.message_processor_passed = message_processor

        self.created_executor = MessageProcessorExecutorSpy()
        return self.created_executor


class MessageProcessorExecutorSpy(MessageProcessorExecutor):
    def __init__(self):
        self.execute_processor_called = False
        self.message_bus_passed = None
        self.message_passed = None
        self.event_passed = None,
        self.data_passed = None

    def execute_processor(self, message_bus, message, event, data):
        self.execute_processor_called = True
        self.message_bus_passed = message_bus
        self.message_passed = message
        self.event_passed = event
        self.data_passed = data


class DummyMessageProcessor(MessageProcessor):
    def get_processor_name(self):
        return "DummyMessageProcessor"

    def generate_unique_id(self):
        return ""

    def parse_data(self, data):
        return None

    def call_business(self, obj):
        return None

    def format_response(self, message_bus, message, event, input_obj, result):
        pass

    def format_exception(self, message_bus, message, event, input_obj, exception):
        pass

    def format_parse_exception(self, message_bus, message, event, data, exception):
        pass


class DummyMessageBus(MessageBus):
    def publish_event(self, event):
        raise NotImplementedError()

    def send_message(self, component, message):
        raise NotImplementedError()

    def subscribe(self, subject):
        raise NotImplementedError()

    def reply_message(self, message, reply_message):
        raise NotImplementedError()

    def reply_event(self, event, reply_message):
        raise NotImplementedError()
