import os
from pathlib import Path
from typing import (
    Callable,
    Dict,
    Optional,
    TypeVar,
    Union,
)

from edpconfigprovider.builder import (
    ProviderBuilder,
    ProviderBuilderFactory,
)
from edpconfigprovider.util import (
    ByTenantDynamicConfigProvider,
    MultiConfigProvider,
    ConfigMapper,
)

T = TypeVar("T")



def build_multi_tenant_app_config_provider(
    env_name: str,
    sub_env_name: str,
    application_name: Optional[str],
    transformer_method: Callable[[Dict], T],
) -> ConfigMapper[T]:

    full_app_name = f"backoffice-{env_name}-{sub_env_name}"
    env_builder = ProviderBuilderFactory.get_app_config_provider_builder(
        application_name=full_app_name,
        profile_name="env",
        environment_name=env_name,
    )
    tenant_builder = ProviderBuilderFactory.get_by_tenant_app_config_provider_builder(
        application_name=full_app_name,
        profile_name_template="tenant-{tenant_name}",
        environment_name=env_name,
    )

    dynamic_builder = ByTenantDynamicConfigProvider(provider_builder=tenant_builder)
    app_builder: Optional[ProviderBuilder] = None
    if application_name is not None:
        app_builder = ProviderBuilderFactory.get_app_config_provider_builder(
            application_name=full_app_name,
            profile_name=f"app-{application_name}",
            environment_name=env_name,
        )

    providers = {
        "env": env_builder.build_provider(),
        "tenant": dynamic_builder,
    }
    if app_builder is not None:
        providers["app"] = app_builder.build_provider()

    multi = MultiConfigProvider(providers=providers)

    return ConfigMapper(
        config_provider=multi,
        transform_method=transformer_method,
    )

def build_json_config_provider(
    env_config_path: Union[str, Path],
    tenant_config_path: Optional[Union[str, Path]],
    app_config_path: Optional[Union[str, Path]],
    transformer_method: Callable[[Dict], T],
) -> ConfigMapper:
    env_config_provider = ProviderBuilderFactory.get_json_provider_builder(json_path=env_config_path).build_provider()
    dynamic_builder = None
    if tenant_config_path is not None:
        tenant_builder = ProviderBuilderFactory.get_by_tenant_json_provider_builder(json_path=tenant_config_path)
        dynamic_builder = ByTenantDynamicConfigProvider(provider_builder=tenant_builder)
    app_config_provider = None
    if app_config_path is not None:
        app_config_provider = ProviderBuilderFactory.get_json_provider_builder(json_path=app_config_path).build_provider()

    providers = {
        "env": env_config_provider,
    }

    if dynamic_builder is not None:
        providers["tenant"] = dynamic_builder

    if app_config_provider is not None:
        providers["app"] = app_config_provider

    multi = MultiConfigProvider(providers=providers)

    return ConfigMapper(
        config_provider=multi,
        transform_method=transformer_method,
    )


def build_multi_tenant_app_config_provider_from_env(
    transformer_method: Callable[[Dict], T],
):
    return build_multi_tenant_app_config_provider(
        env_name=os.environ["APP_CONFIG_ENV_NAME"],
        sub_env_name=os.environ["APP_CONFIG_CLUSTER_NAME"],
        application_name=os.environ.get("APP_CONFIG_APPLICATION_NAME"),
        transformer_method=transformer_method,
    )

def build_json_config_provider_from_env(
    transformer_method: Callable[[Dict], T],
):
    return build_json_config_provider(
        env_config_path=os.environ["ENV_CONFIG_PATH"],
        tenant_config_path=os.environ.get("TENANT_CONFIG_PATH"),
        app_config_path=os.environ.get("APP_CONFIG_PATH"),
        transformer_method=transformer_method,
    )

def build_config_provider_from_env(
    transformer_method: Callable[[Dict], T],
):
    if os.environ.get("ENV_CONFIG_PATH") is not None:
        return build_json_config_provider_from_env(transformer_method=transformer_method)
    elif os.environ.get("APP_CONFIG_ENV_NAME") is not None and os.environ.get("APP_CONFIG_CLUSTER_NAME") is not None:
        return build_multi_tenant_app_config_provider_from_env(transformer_method=transformer_method)
    else:
        raise ValueError("No valid configuration environment variables found")
