from .node import Node
from ...common import AttributeManager

from typing import Any



class _Config:
    __slots__ = ('__config', '__dict__')

    __instance = None


    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super(_Config, cls).__new__(cls)
        return cls.__instance


    def __init__(self, config: Node = None):
        """
        Init Config class.

        :param config: config dict (default={})
        """

        if config is not None:
            self.load(config=config)
        pass


    def load(self, config: Node = None):
        if hasattr(self, AttributeManager.get_name(obj=self, attr='__config')):
            return

        # params init -----
        if config is None:
            config = Node(config={}, read_only=True)

        # init -----
        AttributeManager.set_attribute(obj=self, attr='__config', value=config)
        pass


    def __getattr__(self, name: str) -> Any:
        """
        Get attribute from Loaded config, then from dynamically set at runtime.

        :param name: 'attribute-name'
        :return: attribute-value
        """

        __config = AttributeManager.get_attribute(obj=self, attr='__config')

        if hasattr(__config, name):
            return getattr(__config, name)

        if name not in self.__dict__:
            raise AttributeError(f'Config attribute "{name}" not found')

        return self.__dict__[name]


    def __setattr__(self, name: str, value: Any):
        """
        Allow add new attributes, but not in Loaded config.

        :param name: 'attribute-name'
        :param value: attribute-value
        :return:
        """
        __config = AttributeManager.get_attribute(obj=self, attr='__config')

        if hasattr(__config, name):
            setattr(__config, name, value)
        else:
            self.__dict__[name] = value
        pass


    def __delattr__(self, name: str):
        """
        Allow add new attributes, but not in Loaded config.

        :param name: 'attribute-name'
        :param value: attribute-value
        :return:
        """

        __config = AttributeManager.get_attribute(obj=self, attr='__config')

        if hasattr(__config, name):
            delattr(__config, name)
        else:
            if name not in self.__dict__:
                raise AttributeError(f'Config attribute "{name}" not found')

            del self.__dict__[name]
        pass


    @property
    def as_dict(self) -> dict[str, Any]:
        """
        Conversion of Config[Node] to dict.

        :return:
        """
        __config = AttributeManager.get_attribute(obj=self, attr='__config')
        data = __config.as_dict if __config is not None else {}

        for k, v in self.__dict__.items():
            if not k.startswith('_'):
                data[k] = v
        return data

    pass
