import logging
import inspect

logging.basicConfig(
    style="{",
    encoding="utf-8",
    level=logging.INFO,
    format="{levelname}: {message}",
    force=True,
)

def log_instance(instance, func_name):
    logging.info(
        f"Running: {instance.__class__.__name__}[{getattr(instance, 'name', 'unknown')}].{func_name}"
    )


def log_class_methods(func):
    """Decorator for a class method. Will add a logging entry when the method
    on the class is called.

    Parameters
    ----------
    func : Function
        The function to decorate.
    """

    def wrapper(*args, **kwargs):
        instance = args[0] if len(args) > 0 else None
        func_name = func.__name__
        log_instance(instance, func_name)
        x = func(*args, **kwargs)
        return x

    return wrapper


def apply_to_all(decorator, except_for=[]):
    """Class decorator thats adds a decorator to all methods of a class.

    Parameters
    ----------
    decorator : function
        Decorator function to apply to each method on the class.
    except_for : list, optional
        Do not decorate methods with names defined here, by default [].
    """

    def decorate(cls):
        for attr in cls.__dict__:
            if (
                callable(getattr(cls, attr))
                and not inspect.iscoroutinefunction(getattr(cls, attr))
                and attr not in except_for
                and not isinstance(inspect.getattr_static(cls, attr), staticmethod)
            ):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls

    return decorate