from typing import Any



class AttributeManager:
    __slots__ = ('__obj', '__attr')

    def __init__(self, obj: Any, attr: str):
        self.__obj = obj
        self.__attr = attr
        pass


    @property
    def name(self) -> str:
        return self.get_name(obj=self.__obj, attr=self.__attr)


    @property
    def value(self) -> Any:
        return AttributeManager.get_attribute(obj=self.__obj, attr=self.__attr)


    @value.setter
    def value(self, value: Any):
        AttributeManager.set_attribute(obj=self.__obj, attr=self.__attr, value=value)
        pass


    @property
    def exists(self) -> bool:
        return AttributeManager.has_attribute(obj=self.__obj, attr=self.__attr)


    @staticmethod
    def get_name(obj: Any, attr: str) -> str:
        """
        Build attribute name.

        :param obj: object instance
        :param attr: attribute name
        :return: attribute name (mangled, for private attributes)
        """
        if not attr.startswith('__') or attr.startswith('___') or attr.endswith('__'):
            return attr

        class_name = (
            obj  # cls
            if isinstance(obj, type)
            else obj.__class__  # inst
         ).__name__.lstrip('_')

        return f'_{class_name}{attr}'


    @staticmethod
    def get_attribute(obj: Any, attr: str) -> Any:
        """
        Get an attribute.

        :param obj: object instance
        :param attr: attribute name
        :return: attribute value
        """
        #name = AttributeManager.get_name(obj, attr)
        #try:
        #    return object.__getattribute__(obj, name)
        #except AttributeError:
        #    if attr in obj.__dict__:
        #        return obj.__dict__[attr]
        #    return None
        return object.__getattribute__(
            obj,
            AttributeManager.get_name(obj=obj, attr=attr)
        )


    @staticmethod
    def set_attribute(obj: Any, attr: str, value: Any):
        """
        Set an attribute.

        :param obj: object instance
        :param attr: attribute name
        :param value: attribute value
        :return:
        """
        object.__setattr__(
            obj,
            AttributeManager.get_name(obj=obj, attr=attr),
            value
        )
        pass


    @staticmethod
    def has_attribute(obj: Any, attr: str) -> bool:
        try:
            _ = AttributeManager.get_attribute(obj=obj, attr=attr)
            return True
        except AttributeError:
            return False
        pass

    pass
