import os
import re
from typing import Callable, Dict, Generic, Optional, TypeVar

from edpconfigprovider._utils import get_dict_value
from edpconfigprovider.parameters import ProviderParameters

from ._ProviderBuilder import ProviderBuilder

_PORT_PATTERN = re.compile(r"(:(\d+))(?=/|$)")


T = TypeVar("T")


def _remove_port(context_name):
    return _PORT_PATTERN.sub("", context_name)


def _default_get_context_name(context_name: str) -> str:
    context_name = _remove_port(context_name)
    return context_name


class ContextProviderParameters(ProviderParameters):
    def __init__(self, context_name: str):
        self.context_name = context_name

    def get_parameter(self, parameter_name: str, provider_prefix: Optional[str] = None):
        value = self.builder_parameter_name(provider_prefix, "CONTEXT", parameter_name)
        return value.format(self.context_name)


class ContextConfigProviderBuilder(Generic[T], ProviderBuilder):
    def __init__(
        self,
        transform_method: Optional[Callable[[str, Dict], T]] = None,
        transform_context_name_method: Optional[Callable[[str], str]] = None,
    ) -> None:
        super().__init__()
        self.transform_method = transform_method
        self.transform_context_name_method = (
            transform_context_name_method or _default_get_context_name
        )

        self.context_dict_path = os.environ.get(
            "CONTEXT_CONFIG_PROVIDER_CONFIG_DICT_PATH", "contexts"
        )
        self.context_key = os.environ.get("CONTEXT_CONFIG_PROVIDER_CONFIG_KEY", "key")

    def _parse_context_name(self, context_name: str):
        if self.transform_context_name_method is not None:
            return self.transform_context_name_method(context_name)
        return context_name

    def get_provider_parameters(self, context_name: str) -> ProviderParameters:
        return ContextProviderParameters(context_name=context_name)

    def build_config(self, context_name: str) -> T:
        context_name = self._parse_context_name(context_name)
        config_provider = self.build_provider(context_name=context_name)
        configuration_data = config_provider.get_configuration()

        if configuration_data is not None:
            if (
                len(self.context_dict_path or "") > 0
                and self.context_dict_path in configuration_data
            ):
                configuration_data = get_dict_value(
                    configuration_data, [self.context_dict_path]
                )
                if isinstance(configuration_data, dict):
                    configuration_data = configuration_data.get(context_name)
                elif isinstance(configuration_data, list):
                    context_lookup = {}
                    for ctx in configuration_data:
                        raw_key = ctx.get(self.context_key)
                        if raw_key is None:
                            continue
                        key = self._parse_context_name(raw_key)
                        if key not in context_lookup:
                            context_lookup[key] = ctx
                    configuration_data = context_lookup.get(context_name)

        if self.transform_method is None:
            return configuration_data  # type: ignore
        return self.transform_method(context_name, configuration_data)
