from typing import Optional, Type, Union

from messageprocessor import MessageProcessor
from simplehttprouter import Route

from edpopenapibuilder.builder import OpenApiModelBuilder
from edpopenapibuilder.builder.descriptors import PathModelDescriptor, Descriptors, PayloadDescriptor
from edpopenapibuilder.builder.dto import BuilderConfig
from edpopenapibuilder.builder.impl import OpenApiModelBuilderImpl

_DEFAULT_BUILDER = None
_DEFAULT_BUILDER_CONFIG: Optional[BuilderConfig] = None
_DEFAULT_DESCRIPTORS = None
_DEFAULT_CONFIG: BuilderConfig = {}


def set_default_config(config: BuilderConfig):
    global _DEFAULT_CONFIG
    _DEFAULT_CONFIG = config


def get_descriptors() -> Descriptors:
    global _DEFAULT_DESCRIPTORS
    if _DEFAULT_DESCRIPTORS is None:
        _DEFAULT_DESCRIPTORS = Descriptors()
    return _DEFAULT_DESCRIPTORS


def get_builder(config: Optional[BuilderConfig] = None) -> OpenApiModelBuilder:
    global _DEFAULT_BUILDER, _DEFAULT_BUILDER_CONFIG
    resolved: BuilderConfig = {**_DEFAULT_CONFIG, **(config or {})}
    resolved.setdefault("descriptors", get_descriptors())
    if _DEFAULT_BUILDER is not None and config is not None and resolved != _DEFAULT_BUILDER_CONFIG:
        _DEFAULT_BUILDER = None
    if _DEFAULT_BUILDER is None:
        _DEFAULT_BUILDER_CONFIG = resolved
        _DEFAULT_BUILDER = OpenApiModelBuilderImpl(config=resolved)
    return _DEFAULT_BUILDER


def reset_builder():
    global _DEFAULT_BUILDER, _DEFAULT_BUILDER_CONFIG
    _DEFAULT_BUILDER = None
    _DEFAULT_BUILDER_CONFIG = None


def add_route_descriptor(route: Route, descriptor: PathModelDescriptor):
    get_descriptors().add_route_descriptor(route, descriptor)


def add_processor_descriptor(processor_type: Type[MessageProcessor], descriptor: PathModelDescriptor):
    get_descriptors().add_processor_descriptor(processor_type, descriptor)


def add_interactor_descriptor(interactor_type: Type, descriptor: PathModelDescriptor):
    get_descriptors().add_interactor_descriptor(interactor_type, descriptor)


def add_payload_descriptor(payload_type: Union[Type, str], descriptor: PayloadDescriptor):
    get_descriptors().add_payload_descriptor(payload_type, descriptor)