import pytest
from httpclient import HttpRequest, HttpResponse
from messagebus import HttpRequestMessage, Message, DefaultToken, HttpResponseMessage
from messagehandler import MessageHandler, MessageHandlerBuilder
from testdoubleutil import SpyCall

from httpmessagehandler import HttpMessageHandler, InvalidResponseMessage


class TestHttpMessageHandler(object):
    def test_when_a_request_is_received__a_http_request_message_is_sent_to_the_component(self):
        message_handler = MessageHandlerSpy()
        message_handler_builder = MessageHandlerBuilderStub(message_handler)
        http_message_handler = HttpMessageHandler(message_handler_builder)
        request = HttpRequest("/base/endpoint",
                              "GET",
                              {
                                  "QueryKey": "QueryValue"
                              },
                              {
                                  "Key": "value"
                              },
                              b"body")

        http_message_handler.handle_request(request)
        expected = HttpRequestMessage(request.url, request.method, request.body, request.query, request.headers)
        received = message_handler.handle_message_call.args[1]
        assert expected.url == received.url
        assert expected.body == received.body
        assert expected.method == received.method
        assert expected.query == received.query
        assert expected.headers == received.headers


    class TestParsingResponseWhenRawMessageIsReceived(object):
        @pytest.mark.parametrize('msg', [
            Message(DefaultToken.TK_SYS_ACK),
            Message(DefaultToken.TK_HTTP_MSG),
            Message(DefaultToken.TK_HTTP_MSG, data=b""),
            Message(DefaultToken.TK_HTTP_MSG, data=b"junk"),
            Message(DefaultToken.TK_HTTP_MSG, data=b"200"),
            Message(DefaultToken.TK_HTTP_MSG, data=b"200\01")
        ])
        def test__when_invalid_message_is_received__invalid_response_message_is_raised(self, msg):
            message_handler = MessageHandlerStub(msg)
            message_handler_builder = MessageHandlerBuilderStub(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")

            with pytest.raises(InvalidResponseMessage):
                http_message_handler.handle_request(request)

        def test__when_a_valid_message_is_received__http_response_is_returned(self):
            response_msg = Message(DefaultToken.TK_HTTP_MSG, data=b"200\0001\0Content-Type\0application/json\0body")
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderStub(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            response = http_message_handler.handle_request(request)
            assert response == HttpResponse(200, b"body", {"Content-Type": "application/json"})

        def test__when_body_has_delimiter__http_response_is_returned(self):
            response_msg = Message(DefaultToken.TK_HTTP_MSG,
                                   data=b"200\0001\0Content-Type\0application/json\0body\0body\0body")
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderStub(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            response = http_message_handler.handle_request(request)
            assert response == HttpResponse(200, b"body\0body\0body", {"Content-Type": "application/json"})

    class TestParsingResponseWhenHttpResponseMessageMessageIsReceived(object):
        def test__when_http_response_message_is_received__http_response_is_returned(self):
            response_msg = HttpResponseMessage(200, b"body", {"Content-Type": "application/json"})
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderStub(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            response = http_message_handler.handle_request(request)
            assert response == HttpResponse(200, b"body", {"Content-Type": "application/json"})

        def test__when_body_has_delimiter__http_response_is_returned(self):
            response_msg = HttpResponseMessage(200, b"body\0body\0body", {"Content-Type": "application/json"})
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderStub(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            response = http_message_handler.handle_request(request)
            assert response == HttpResponse(200, b"body\0body\0body", {"Content-Type": "application/json"})

    def test_when_an_exception_is_raised_by_the_message_handler__an_internal_server_error_is_returned(self):
        message_handler = MessageHandlerStub(Exception("Error"))
        message_handler_builder = MessageHandlerBuilderStub(message_handler)
        http_message_handler = HttpMessageHandler(message_handler_builder)
        request = HttpRequest("/base/endpoint", "GET")
        response = http_message_handler.handle_request(request)
        assert response == HttpResponse(500, 'Internal Server Error: Error'.encode("utf-8"), {})

    class TestMessageHandlerLifeCycle(object):
        def test__message_handler_builder_receives_the_http_request_message(self):
            response_msg = HttpResponseMessage(200, b"body", {"Content-Type": "application/json"})
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderSpy(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            http_message_handler.handle_request(request)
            assert\
                message_handler_builder.build_message_handler_call.args[0] == \
                HttpRequestMessage(request.url, request.method, request.body, request.headers)

        def test__when_successfully_processed__message_handler_is_destroyed(self):
            response_msg = HttpResponseMessage(200, b"body", {"Content-Type": "application/json"})
            message_handler = MessageHandlerStub(response_msg)
            message_handler_builder = MessageHandlerBuilderSpy(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            http_message_handler.handle_request(request)
            assert message_handler_builder.destroy_message_handler_call.args[0] == message_handler

        def test__when_an_exception_is_raised_by_the_message_handler__message_handler_is_destroyed(self):
            message_handler = MessageHandlerStub(Exception("Error"))
            message_handler_builder = MessageHandlerBuilderSpy(message_handler)
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            http_message_handler.handle_request(request)
            assert message_handler_builder.destroy_message_handler_call.args[0] == message_handler

        def test_when_an_exception_is_raised_by_the_builder__message_handler_is_not_destroyed(self):
            message_handler_builder = MessageHandlerBuilderSpy(Exception("Error"))
            http_message_handler = HttpMessageHandler(message_handler_builder)
            request = HttpRequest("/base/endpoint", "GET")
            http_message_handler.handle_request(request)
            assert not message_handler_builder.destroy_message_handler_call.called


class DefaultStub(object):
    def __init__(self, response=None):
        self.response = response

    def method(self, *args, **kwargs):
        if isinstance(self.response, Exception):
            raise self.response
        return self.response


class MessageHandlerStub(MessageHandler):
    def __init__(self, handle_message_response=None):
        if handle_message_response is None:
            handle_message_response = HttpResponseMessage(200)
        self.handle_message_stub = DefaultStub(handle_message_response)

    def handle_event(self, message_bus, event):
        pass

    def handle_message(self, message_bus, message):
        message_bus.reply_message(message, self.handle_message_stub.method(message_bus, message))


class MessageHandlerSpy(MessageHandlerStub):
    def __init__(self, handle_message_response=None):
        super(MessageHandlerSpy, self).__init__(handle_message_response)
        self.handle_message_call = SpyCall()

    def handle_event(self, message_bus, event):
        pass

    def handle_message(self, message_bus, message):
        self.handle_message_call.register(message_bus, message)
        super(MessageHandlerSpy, self).handle_message(message_bus, message)


class MessageHandlerBuilderStub(MessageHandlerBuilder):
    def __init__(self, message_handler):
        self.message_handler = DefaultStub(message_handler)

    def build_singletons(self):
        pass

    def build_message_handler(self, message, event):
        return self.message_handler.method(message, event)

    def destroy_message_handler(self, message_handler):
        pass

    def destroy_singletons(self):
        pass


class MessageHandlerBuilderSpy(MessageHandlerBuilderStub):
    def __init__(self, message_handler):
        super(MessageHandlerBuilderSpy, self).__init__(message_handler)
        self.build_message_handler_call = SpyCall()
        self.destroy_message_handler_call = SpyCall()

    def build_singletons(self):
        pass

    def build_message_handler(self, message, event):
        self.build_message_handler_call.register(message, event)
        return super(MessageHandlerBuilderSpy, self).build_message_handler(message, event)

    def destroy_message_handler(self, message_handler):
        self.destroy_message_handler_call.register(message_handler)

    def destroy_singletons(self):
        pass
