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

from edpconfigprovider.util import ConfigMapper
from edpconfigprovider.util.environment import EnvTenantConfigFactory
from messagebus import (
    MessageBus,
)
from messagehandler import (
    MessageHandler,
)

from ._ConfigProviderMessageHandlerBuilder import ConfigProviderMessageHandlerBuilder

T = TypeVar("T")
U = TypeVar("U")

logger = logging.getLogger(__name__)


class MultiTenantConfigProviderMessageHandlerBuilder(ConfigProviderMessageHandlerBuilder, Generic[T, U], ABC):
    def __init__(
        self,
        config_transformer: ConfigMapper[T],
        tenant_config_factory: EnvTenantConfigFactory[U],
        message_bus: Optional[MessageBus] = None,
        tenant_mandatory: bool = True,
        exception_handler: Optional[Callable[[Exception], Any]] = None,
    ):
        super().__init__(
            config_transformer=config_transformer,
            message_bus=message_bus,
            tenant_mandatory=tenant_mandatory,
            exception_handler=exception_handler,
        )
        self.tenant_config_factory = tenant_config_factory
        self.tenant_dict: Dict[str, Optional[Any]] = {}

        self._cache_lock = threading.Lock()
        self._lock_dict = {}

    def _get_tenant_lock(self, tenant_name: str) -> threading.Lock:
        with self._cache_lock:
            if tenant_name not in self._lock_dict:
                self._lock_dict[tenant_name] = threading.Lock()
            return self._lock_dict[tenant_name]

    def get_tenant_config(self, tenant_name: Optional[str]) -> Optional[U]:
        if tenant_name is None:
            return None

        if tenant_name not in self.tenant_dict:
            with self._get_tenant_lock(tenant_name):
                if tenant_name not in self.tenant_dict:
                    tenant_data = self.build_tenant(tenant_name)
                    if tenant_data is None:
                        logger.error("build_tenant returned None for tenant '%s'. Skipping cache.", tenant_name)
                        return None
                    self.tenant_dict[tenant_name] = tenant_data

        return self.tenant_dict[tenant_name]

    def build_message_handler_with_config(self, config: T) -> MessageHandler:
        try:
            current_thread = threading.current_thread()
            tenant_name = getattr(current_thread, '_edp_tenant_name')

            tenant_config = self.get_tenant_config(tenant_name)
            if tenant_config is not None:
                message_handler = self.build_message_handler_with_tenant_config(config, tenant_config)
                return message_handler

            raise Exception("No tenant")
        except Exception as e:
            return self.handle_exception(e)

    @abstractmethod
    def build_message_handler_with_tenant_config(self, config: T, tenant_config: U) -> MessageHandler:
        raise NotImplementedError()

    def build_tenant(self, tenant_name: str) -> Any:
        return self.tenant_config_factory.build_config(tenant_name)
