from unittest import TestCase

import testhttpmodule
from edpdependencyinjection import DependencyLoader
from dihttpmessageprocessor import DiRouterProcessorBuilder
from httpmessageprocessor import RouteNotMapped
from messageprocessor import MessageProcessor
from simplehttprouter import Route
from testhttpmodule.interactor import TestInteractor
from testhttpmodule.processor import SimpleProcessor


class TestDiRouterProcessorBuilder(TestCase):

    def setUp(self):
        self.dependencies_loader = DependencyLoader()
        self.dependencies_loader.load_modules(modules=[testhttpmodule])
        self.dependency_context = self.dependencies_loader.build_context()

    def test_get_processor_resolves_type_via_di(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): SimpleProcessor},
        )
        found_route, processor = builder.get_processor(Route("GET", "/simple"))
        self.assertIsNotNone(found_route)
        self.assertIsInstance(processor, SimpleProcessor)

    def test_get_processor_returns_instance_directly(self):
        instance = SimpleProcessor()
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): instance},
        )
        found_route, processor = builder.get_processor(Route("GET", "/simple"))
        self.assertIs(processor, instance)

    def test_get_processor_raises_route_not_mapped(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): SimpleProcessor},
        )
        with self.assertRaises(RouteNotMapped):
            builder.get_processor(Route("GET", "/unknown"))

    def test_get_processor_with_wrong_method_raises(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): SimpleProcessor},
        )
        with self.assertRaises(RouteNotMapped):
            builder.get_processor(Route("POST", "/simple"))

    def test_get_processor_parametrized_route(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/items/{id}"): SimpleProcessor},
        )
        found_route, processor = builder.get_processor(Route("GET", "/items/42"))
        self.assertIsNotNone(found_route)
        self.assertIsInstance(processor, SimpleProcessor)

    def test_instance_returns_same_object_every_call(self):
        instance = SimpleProcessor()
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): instance},
        )
        _, processor1 = builder.get_processor(Route("GET", "/simple"))
        _, processor2 = builder.get_processor(Route("GET", "/simple"))
        self.assertIs(processor1, processor2)

    def test_builder_registers_itself_in_di_context(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={Route("GET", "/simple"): SimpleProcessor},
        )
        resolved = self.dependency_context.get_instance(DiRouterProcessorBuilder)
        self.assertIs(resolved, builder)

    def test_multiple_routes(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={
                Route("GET", "/a"): SimpleProcessor,
                Route("POST", "/b"): SimpleProcessor,
            },
        )
        found_a, proc_a = builder.get_processor(Route("GET", "/a"))
        found_b, proc_b = builder.get_processor(Route("POST", "/b"))
        self.assertIsInstance(proc_a, SimpleProcessor)
        self.assertIsInstance(proc_b, SimpleProcessor)

    def test_empty_route_definition_always_raises(self):
        builder = DiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition={},
        )
        with self.assertRaises(RouteNotMapped):
            builder.get_processor(Route("GET", "/anything"))
