from typing import Dict, Type, Union

from simplehttprouter import Route, SimpleHttpRouter

from dependencyinjection import DependencyContext
from httpmessageprocessor import RouteNotMapped
from messageprocessor import MessageProcessor
from ._AutoMappedRouteBuilder import AutoMappedRouteBuilder
from ._AutoMappedWebApi import AutoMappedWebApi
from ._DiRouterProcessorBuilder import DiRouterProcessorBuilder


class AutoMappedDiRouterProcessorBuilder(DiRouterProcessorBuilder):
    def __init__(
        self,
        dependency_context: DependencyContext,
        route_definition: Dict[Route, Union[Type[MessageProcessor], AutoMappedWebApi]] = None,
    ):
        self.auto_mapped_definition: Dict[Route, AutoMappedWebApi] = {}
        self.processor_mapped_definition: Dict[Route, Type[MessageProcessor]] = {}
        self.dependency_context = dependency_context

        self._build_routes_definitions(route_definition)
        self._get_builder_dependency_routes()

        super().__init__(dependency_context, self.processor_mapped_definition)
        self.auto_mapped_router = SimpleHttpRouter(list(self.auto_mapped_definition.keys()))

    def _build_routes_definitions(self, route_definition):
        if route_definition is not None:
            for route, value in route_definition.items():
                if isinstance(value, AutoMappedWebApi):
                    self.auto_mapped_definition[route] = value
                else:
                    self.processor_mapped_definition[route] = value

    def _get_builder_dependency_routes(self):
        builder_types = self.dependency_context.get_dependencies_by_parent(AutoMappedRouteBuilder)
        for builder_type in builder_types:
            builder_instance = self.dependency_context.get_instance(builder_type)
            self._build_routes_definitions(builder_instance.build_routes())

    def get_processor(self, route):
        found_route = self.router.get_route(route)
        if found_route is not None:
            return super().get_processor(route)

        found_route = self.auto_mapped_router.get_route(route)
        if found_route is not None:
            definition = self.auto_mapped_definition[found_route.route]

            interactor = self.dependency_context.get_instance(definition.interactor_type)
            processor = definition.processor_type(
                request_type=definition.request_type,
                interactor=interactor,
                exception_map=definition.exception_map,
                success_status=definition.success_status,
                extra_mapping=definition.extra_mapping,
                event_name=definition.event_name,
                response_content_type=definition.response_content_type,
            )
            return found_route, processor

        raise RouteNotMapped(route.method, route.path)
