import abc

from ziplime.errors import AccountControlViolation


class AccountControl(metaclass=abc.ABCMeta):
    """Abstract base class representing a fail-safe control on the behavior of any
    algorithm.
    """

    def __init__(self, **kwargs):
        """Track any arguments that should be printed in the error message
        generated by self.fail.
        """
        self.__fail_args = kwargs

    @abc.abstractmethod
    def validate(self, _portfolio, _account, _algo_datetime, _algo_current_data):
        """On each call to handle data by TradingAlgorithm, this method should be
        called *exactly once* on each registered AccountControl object.

        If the check does not violate this AccountControl's restraint given
        the information in `portfolio` and `account`, this method should
        return None and have no externally-visible side-effects.

        If the desired order violates this AccountControl's constraint, this
        method should call self.fail().
        """
        raise NotImplementedError

    def fail(self):
        """Raise an AccountControlViolation with information about the failure."""
        raise AccountControlViolation(constraint=repr(self))

    def __repr__(self):
        return "{name}({attrs})".format(
            name=self.__class__.__name__, attrs=self.__fail_args
        )
