from pathlib import Path
from typing import Union, Optional

from edpconfigprovider import ConfigProvider
from edpconfigprovider.builder import ProviderBuilder
from edpconfigprovider.impl.json import JsonConfigProvider


class JsonConfigProviderBuilder(ProviderBuilder):
    def __init__(self, json_path: Union[str, Path]) -> None:
        super().__init__()
        self.json_path = json_path

    def build_provider(self, **kwargs) -> ConfigProvider:
        return JsonConfigProvider(json_path=self.json_path)


class ByTenantJsonConfigProviderBuilder(ProviderBuilder):
    def __init__(
        self,
        json_path: Union[str, Path],
    ) -> None:
        super().__init__()
        self.json_path = json_path
        self.full_json = JsonConfigProviderBuilder(json_path=self.json_path).build_provider().get_configuration()

    def build_provider(self, **kwargs) -> ConfigProvider:
        if "tenant_name" not in kwargs:
            raise ValueError("tenant_name must be provided")

        tenant_name = kwargs["tenant_name"]

        return JsonConfigProvider(json_path=self.full_json[tenant_name])


class AppConfigProviderBuilder(ProviderBuilder):
    def __init__(
        self,
        application_name: str,
        profile_name: str,
        environment_name: str,
    ) -> None:
        super().__init__()
        self.application_name = application_name
        self.profile_name = profile_name
        self.environment_name = environment_name

    def build_provider(self, **kwargs) -> ConfigProvider:
        from edpconfigprovider.impl.aws.appconfig import AwsAppConfigProvider

        return AwsAppConfigProvider(
            application_name=self.application_name,
            profile_name=self.profile_name,
            environment_name=self.environment_name,
        )

class ByTenantAppConfigProviderBuilder(ProviderBuilder):
    def __init__(
        self,
        application_name: str,
        profile_name_template: str,
        environment_name: str,
    ) -> None:
        super().__init__()
        self.application_name = application_name
        self.profile_name_template = profile_name_template
        self.environment_name = environment_name

    def build_provider(self, **kwargs) -> ConfigProvider:
        from edpconfigprovider.impl.aws.appconfig import AwsAppConfigProvider

        if "tenant_name" not in kwargs:
            raise ValueError("tenant_name must be provided")

        tenant_name = kwargs["tenant_name"]

        return AwsAppConfigProvider(
            application_name=self.application_name,
            profile_name=self.profile_name_template.format(tenant_name=tenant_name),
            environment_name=self.environment_name,
        )


class AppConfigAgentProviderBuilder(ProviderBuilder):
    def __init__(
        self,
        application_name: str,
        profile_name: str,
        environment_name: str,
        port: Optional[int] = None,
    ) -> None:
        super().__init__()
        self.application_name = application_name
        self.profile_name = profile_name
        self.environment_name = environment_name
        self.port = port

    def build_provider(self, **kwargs) -> ConfigProvider:
        from edpconfigprovider.impl.aws.appconfig import AwsAppconfigAgentProvider

        return AwsAppconfigAgentProvider(
            application_name=self.application_name,
            profile_name=self.profile_name,
            environment_name=self.environment_name,
            port=self.port
        )

class ByTenantAppConfigAgentProviderBuilder(ProviderBuilder):
    def __init__(
        self,
        application_name_template: str,
        profile_name: str,
        environment_name: str,
        port: Optional[int] = None,
    ) -> None:
        super().__init__()
        self.application_name_template = application_name_template
        self.profile_name = profile_name
        self.environment_name = environment_name
        self.port = port

    def build_provider(self, **kwargs) -> ConfigProvider:
        from edpconfigprovider.impl.aws.appconfig import AwsAppconfigAgentProvider

        if "tenant_name" not in kwargs:
            raise ValueError("tenant_name must be provided")

        tenant_name = kwargs["tenant_name"]

        return AwsAppconfigAgentProvider(
            application_name=self.application_name_template.format(tenant_name=tenant_name),
            profile_name=self.profile_name,
            environment_name=self.environment_name,
            port=self.port,
        )

class ProviderBuilderFactory:
    @staticmethod
    def get_json_provider_builder(json_path: Union[str, Path]) -> ProviderBuilder:
        return JsonConfigProviderBuilder(json_path=json_path)

    @staticmethod
    def get_by_tenant_json_provider_builder(json_path: Union[str, Path]) -> ProviderBuilder:
        return ByTenantJsonConfigProviderBuilder(json_path=json_path)

    @staticmethod
    def get_app_config_provider_builder(
        application_name: str,
        profile_name: str,
        environment_name: str,
    ):
        return AppConfigProviderBuilder(
            application_name=application_name,
            profile_name=profile_name,
            environment_name=environment_name,
        )

    @staticmethod
    def get_by_tenant_app_config_provider_builder(
        application_name: str,
        profile_name_template: str,
        environment_name: str,
    ):
        return ByTenantAppConfigProviderBuilder(
            application_name=application_name,
            profile_name_template=profile_name_template,
            environment_name=environment_name,
        )

    @staticmethod
    def get_app_config_agent_provider_builder(
        application_name: str,
        profile_name: str,
        environment_name: str,
        port: Optional[int] = None,
    ):
        return AppConfigAgentProviderBuilder(
            application_name=application_name,
            profile_name=profile_name,
            environment_name=environment_name,
            port=port,
        )

    @staticmethod
    def get_by_tenant_app_config_agent_provider_builder(
        application_name_template: str,
        profile_name: str,
        environment_name: str,
        port: Optional[int] = None,
    ):
        return ByTenantAppConfigAgentProviderBuilder(
            application_name_template=application_name_template,
            profile_name=profile_name,
            environment_name=environment_name,
            port=port,
        )
