from typing import Generic, Dict, Callable, Optional

from ._EnvConfigProviderBuilder import EnvConfigProviderBuilder
from ._providers import ProviderParameters, T
from .._ConfigMapper import ConfigMapper
from ... import ConfigProvider
from ...exceptions import ConfigProviderException


class MissingTenantConfigurationException(ConfigProviderException):
    ...


class TenantConfigMapper(ConfigMapper, Generic[T]):
    def __init__(
        self,
        tenant_name: str,
        parameters: ProviderParameters,
        config_provider: ConfigProvider,
        transform_method: Callable[[Dict], T]
    ):
        super().__init__(config_provider, transform_method)
        self.tenant_name = tenant_name
        self.parameters = parameters

    def build_config(self) -> T:
        configuration_data = self.config_provider.get_configuration()
        multi_config = self.parameters.get_bool_parameter("multi_config", default_value=False)
        base_path = self.parameters.get_parameter("base_path")
        key = self.parameters.get_parameter("key", default_value="key")

        if multi_config:
            if base_path:
                for path in base_path.split(","):
                    configuration_data = configuration_data[path]

            if isinstance(configuration_data, list):
                configuration_data = next((c for c in configuration_data if c.get(key) == self.tenant_name), None)
            else:
                configuration_data = configuration_data.get(self.tenant_name)

            if configuration_data is None:
                raise MissingTenantConfigurationException(f"Missing tenant {self.tenant_name} configuration")

        return self.transform_method(configuration_data)


class EnvTenantConfigProviderBuilder(EnvConfigProviderBuilder, Generic[T]):
    def _get_builder_parameters(self, tenant_name: str, **kwargs) -> ProviderParameters:
        kwargs["replace_parameters"] = {
            **(kwargs.get("replace_parameters") or {}),
            "tenant_name": tenant_name,
        }
        kwargs.setdefault("config_prefix", "TENANT")
        return super()._get_builder_parameters(**kwargs)

    def build_provider(self, tenant_name: str, **kwargs) -> ConfigProvider:
        return super().build_provider(tenant_name=tenant_name, **kwargs)

    def build_config_mapper(self, tenant_name: str, **kwargs) -> ConfigMapper[T]:
        return TenantConfigMapper(
            tenant_name=tenant_name,
            parameters=self._get_builder_parameters(tenant_name=tenant_name, **kwargs),
            config_provider=self.build_provider(tenant_name=tenant_name, **kwargs),
            transform_method=self.transform_method
        )

    def build_config(self, tenant_name: str, **kwargs) -> T:
        return super().build_config(tenant_name=tenant_name, **kwargs)


class EnvTenantConfigFactory(Generic[T]):
    def __init__(self, transform_method: Optional[Callable[[Dict], T]] = None, **kwargs):
        self.provider_builder = EnvTenantConfigProviderBuilder(transform_method=transform_method)
        self._kwargs = kwargs

    def build_config(self, tenant_name: str) -> T:
        return self.provider_builder.build_config(tenant_name=tenant_name, **self._kwargs)
