import logging
import os
from typing import List

from risc_lasagna.helper import print_dict_like_list
from risc_lasagna.layer.dotenv_layer import _DotEnvLayer
from risc_lasagna.layer.environment_layer import _EnvironmentLayer
from risc_lasagna.layer.typedef import Layer
from risc_lasagna.layer.yaml_layer import _YamlLayer
from risc_lasagna.typedef import ConfigurationError

logger = logging.getLogger(__name__)


class AggregationLayer(Layer):
    """
    This layer accept a list of layer. These then act as a single 'layer'. The ordering decides which variable
    from which layer takes priority. (first in the list overwrites all the ones after that).
    """

    def __init__(self, layers: List[Layer]):
        """
        :param layers: A list of layer which are used to lookup values. Layer are consulted in the given order.
        Meaning the first layer overwrites any layer after it.
        """
        layer_types = [type(layer) for layer in layers]
        if _DotEnvLayer in layer_types and _YamlLayer in layer_types:
            raise ConfigurationError(
                "DotEnv and Yaml layer are not compatible! Please use one or the other."
            )
        if _YamlLayer in layer_types and _EnvironmentLayer in layer_types:
            logger.info(
                "Yaml layer and environment layer are only partially compatible. "
                "Lists can not be defined via environment variables and yaml is case sensitive while the environment "
                "is not by default!"
            )

        self._layers = layers

    def get(self, key):
        for layer in self._layers:
            value = layer.get(key)
            if value is not None:
                return value
        return None

    def keys(self) -> List[str]:
        keys = set()
        for layer in self._layers:
            keys.update(layer.keys())
        return list(keys)

    def contains(self, key: str) -> bool:
        for layer in self._layers:
            if layer.contains(key):
                return True
        return False

    def __repr__(self):
        """
        Output the aggregated layer and a list of internal layer.
        """
        # show computed what the layer contains
        map_ = {key: self.get(key) for key in self.keys()}
        repr_ = print_dict_like_list(map_, title=self.__class__.__name__)

        repr_ = f"{repr_}{os.linesep}... based on ...{os.linesep * 2}"

        # show each layer
        for index, layer in enumerate(self._layers, start=-len(self._layers)):
            repr_ = f"{repr_}Layer {abs(index)} {repr(layer)}"
        return repr_
