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

import testhttpmodule
from edpdependencyinjection import DependencyLoader
from dihttpmessageprocessor import AutoMappedWebApi, AutoMappedDiRouterProcessorBuilder
from httpmessageprocessor import RouteNotMapped
from httpmessageprocessorutil.automapped import AutoMappedMessageProcessor
from messageprocessor import MessageProcessor
from simplehttprouter import Route
from testhttpmodule.interactor import BuilderInteractor, TestInteractor
from testhttpmodule.processor import (
    SimpleProcessor,
    TestBuildProcessor,
    TestProcessorWithParameter
)

from dihttpmessageprocessor import (
    AutoMappedDiRouterProcessorBuilder,
    AutoMappedWebApi,
    AcceptableRoutesImplementations
)


class TestAutoMappedDiRouterProcessorBuilder(TestCase):

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

    def _get_router_builder(
        self,
        route_definition: Mapping[Route, AcceptableRoutesImplementations] = None,
    ) -> AutoMappedDiRouterProcessorBuilder:
        router_builder = AutoMappedDiRouterProcessorBuilder(
            dependency_context=self.dependency_context,
            route_definition=route_definition or {},
        )
        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(getattr(processor_instance, "interactor"), TestInteractor),
            "Interactor Instance is different",
        )

    def test_expected_route_with_automapped_processor_instance(self):
        route_definition = {
            Route("GET", "/test"): AutoMappedMessageProcessor(
                request_type=None,
                interactor=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(getattr(processor_instance, "interactor"), TestInteractor),
            "Interactor Instance is different",
        )


    def test_expected_route_with_simple_processor_instance(self):
        route_definition = {
            Route("GET", "/test"): SimpleProcessor()
        }
        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, SimpleProcessor),
            "Processor 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(getattr(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(getattr(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(getattr(processor_instance, "interactor"), TestInteractor),
            "Interactor Instance is different",
        )

    def test_expected_route_with_parameter(self):
        self.dependency_context.add_dependency_extra_parameter(
            parameter_name="injected_parameter",
            parameter_value="InjectedValue",
        )
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test-with-parameter"))
        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, TestProcessorWithParameter),
            "Processor Instance is different",
        )
        self.assertTrue(
            isinstance(getattr(processor_instance, "interactor"), TestInteractor),
            "Interactor Instance is different",
        )
        self.assertEqual(
            getattr(processor_instance, "injected_parameter"),
            "InjectedValue",
            "Injected Value is different",
        )

    def test_expected_route_with_parameter_auto(self):
        self.dependency_context.add_dependency_extra_parameter(
            parameter_name="injected_parameter",
            parameter_value="InjectedValueAuto",
        )
        router_builder = self._get_router_builder()
        found_route, processor_instance = router_builder.get_processor(Route("GET", "/test-with-parameter-auto"))
        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, TestProcessorWithParameter),
            "Processor Instance is different",
        )
        self.assertTrue(
            isinstance(getattr(processor_instance, "interactor"), BuilderInteractor),
            "Interactor Instance is different",
        )
        self.assertEqual(
            getattr(processor_instance, "injected_parameter"),
            "InjectedValueAuto",
            "Injected Value is different",
        )
