from typing import Dict, Union, Type
from unittest import TestCase

from simplehttprouter import Route

import testhttpmodule
from dependencyinjection import DependencyLoader
from dihttpmessageprocessor import AutoMappedWebApi, AutoMappedDiRouterProcessorBuilder
from httpmessageprocessor import RouteNotMapped
from httpmessageprocessorutil.automapped import AutoMappedMessageProcessor
from messageprocessor import MessageProcessor
from testhttpmodule.interactor import TestInteractor, BuilderInteractor
from testhttpmodule.processor import SimpleProcessor, TestBuildProcessor


class TestAutoMappedDiRouterProcessorBuilder(TestCase):

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

    def _get_router_builder(
            self,
            route_definition: Dict[Route, Union[Type[MessageProcessor], AutoMappedWebApi]] = None
    ) -> AutoMappedDiRouterProcessorBuilder:
        router_builder = AutoMappedDiRouterProcessorBuilder(
            dependency_context=self.dependencies_loader.build_context(),
            route_definition=route_definition,
        )
        return router_builder

    def test_get_simple_processor(self):
        route_definition = {
            Route("GET", "/simple"): SimpleProcessor
        }
        router_builder = self._get_router_builder(route_definition)
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/simple"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, SimpleProcessor), "Processor Instance is different")

    def test_expected_route_not_found(self):
        route_definition = {
            Route("GET", "/simple"): SimpleProcessor
        }
        router_builder = self._get_router_builder(route_definition)
        with self.assertRaises(RouteNotMapped):
            router_builder.get_processor(Route("GET", "/not_simple"))

    def test_expected_route_with_web_api_definition(self):
        route_definition = {
            Route("GET", "/test"): AutoMappedWebApi(
                interactor_type=TestInteractor
            )
        }
        router_builder = self._get_router_builder(route_definition)
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, AutoMappedMessageProcessor), "Processor Instance is different")
        self.assertTrue(isinstance(processor_instance.interactor, TestInteractor), "Interactor Instance is different")

    def test_expected_route_with_builder_definition(self):
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/build"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, AutoMappedMessageProcessor), "Processor Instance is different")
        self.assertTrue(isinstance(processor_instance.interactor, BuilderInteractor),
                        "Interactor Instance is different")

    def test_expected_route_with_builder_processor_definition(self):
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test-build"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, TestBuildProcessor), "Processor Instance is different")

    def test_expected_route_with_builder_definition_parametrized(self):
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/build/1"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, AutoMappedMessageProcessor), "Processor Instance is different")
        self.assertTrue(isinstance(processor_instance.interactor, BuilderInteractor),
                        "Interactor Instance is different")

    def test_expected_route_with_builder_processor_definition_parametrized(self):
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test-build/1"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, TestBuildProcessor), "Processor Instance is different")

    def test_get_simple_processor_parametrized(self):
        route_definition = {
            Route("GET", "/simple/{param}"): SimpleProcessor
        }
        router_builder = self._get_router_builder(route_definition)
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/simple/1"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, SimpleProcessor), "Processor Instance is different")

    def test_expected_route_with_web_api_definition_parametrized(self):
        route_definition = {
            Route("GET", "/test/{param}"): AutoMappedWebApi(
                interactor_type=TestInteractor
            )
        }
        router_builder = self._get_router_builder(route_definition)
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test/1"))
        self.assertTrue(processor_instance is not None, "Processor Instance is None")
        self.assertTrue(found_route is not None, "Found Route is None")
        self.assertTrue(isinstance(processor_instance, AutoMappedMessageProcessor), "Processor Instance is different")
        self.assertTrue(isinstance(processor_instance.interactor, TestInteractor), "Interactor Instance is different")
