from typing import List, Generic, Callable, Dict, Optional

from ... import ConfigProvider
from ...cache import build_cache_from_env, wrap_provider
from .._FallbackConfigProvider import FallbackConfigProvider
from .._ConfigMapper import ConfigMapper

from ._providers import get_provider, ProviderParameters, T


def _default_dict_transform(config: Dict) -> Dict:
    return config


class EnvConfigProviderBuilder(Generic[T]):
    def __init__(self, transform_method: Optional[Callable[[Dict], T]] = None):
        self.transform_method = transform_method or _default_dict_transform
        # Built once per builder so a reused factory (EnvTenantConfigFactory) does
        # not create a new Redis client/connection pool on every build_config call.
        self._cache = build_cache_from_env()

    @staticmethod
    def _get_provider_types(parameters: ProviderParameters) -> List[str]:
        config_provider_type = parameters.get_parameter(
            parameter_name="config_provider_type",
            default_value="json",
            fallback_without_prefix=True,
        )
        return [t.strip().lower() for t in config_provider_type.split(",")]

    def _build_config_provider(self, parameters: ProviderParameters) -> ConfigProvider:
        provider_types = self._get_provider_types(parameters)

        if len(provider_types) == 1:
            return wrap_provider(get_provider(provider_types[0], parameters), self._cache)

        providers = [wrap_provider(get_provider(pt, parameters), self._cache) for pt in provider_types]
        return FallbackConfigProvider(providers)

    def _get_builder_parameters(self, **kwargs) -> ProviderParameters:
        parameters = ProviderParameters(**kwargs)
        return parameters

    def build_provider(self, **kwargs) -> ConfigProvider:
        parameters = self._get_builder_parameters(**kwargs)
        return self._build_config_provider(parameters)

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

    def build_config(self, **kwargs) -> T:
        config_mapper = self.build_config_mapper(**kwargs)
        return config_mapper.build_config()
