import logging
import threading
from abc import (
    ABC,
    abstractmethod,
)
from typing import (
    Generic,
    Optional,
    TypeVar,
)

from contextmessagebus import MultiContextMessageBus
from messagebus import (
    Event,
    Message,
    MessageBus,
)
from messagehandler import (
    MessageHandler,
    MessageHandlerBuilder,
)

from edpconfigprovider.util import ConfigMapper

T = TypeVar("T")


logger = logging.getLogger(__name__)


class ConfigProviderMessageHandlerBuilder(Generic[T], MessageHandlerBuilder, ABC):
    def __init__(
        self,
        config_transformer: ConfigMapper[T],
        message_bus: Optional[MessageBus] = None,
        tenant_mandatory: bool = True,
    ):
        self.config_transformer = config_transformer
        self.message_bus = message_bus
        self.tenant_mandatory = tenant_mandatory

    def build_message_handler(
        self,
        message: Optional[Message],
        event: Optional[Event],
    ) -> MessageHandler:
        tenant_name = self._get_tenant_name(message, event)

        if tenant_name is None:
            if self.tenant_mandatory:
                logger.error("The 'tenant' was not present in the message or event: message: %s event: %s", message, event)
                raise RuntimeError("No tenant")
        else:
            current_thread = threading.current_thread()
            setattr(current_thread, '_edp_tenant_name', tenant_name)
            setattr(current_thread, '_edp_context_name', tenant_name)
            if isinstance(self.message_bus, MultiContextMessageBus):
                self.message_bus.set_current_context(tenant_name)

        return self.build_message_handler_with_config(config=self.config_transformer.build_config())


    @abstractmethod
    def build_message_handler_with_config(self, config: T) -> MessageHandler:
        raise NotImplementedError()

    @staticmethod
    def _get_tenant_name(message, event):
        headers = message.headers if message is not None else event.headers
        tenant = headers.get("tenant")
        if tenant is not None:
            return tenant
        return headers.get("context")
