import abc
from collections.abc import Callable, Mapping

from .types import FunctionType, OperatorType, ValueType


class Expression(abc.ABC):
    _evaluator: Callable[[Mapping[str, ValueType]], ValueType]

    def evaluate(self, variables: Mapping[str, ValueType]):
        return self._evaluator(variables)


class ValueExpression(Expression):
    def __init__(self, a: str | ValueType):
        self.__debug_a = a

        if isinstance(a, str):
            self._evaluator = lambda vars: vars[a]
        else:
            self._evaluator = lambda _: a

    def __repr__(self):
        return f"<{self.__debug_a}>"


class UnaryExpression(Expression):
    def __init__(self, function: FunctionType, a: Expression):
        self.__debug_f = function.__name__
        self.__debug_a = repr(a)

        self._evaluator = lambda vars: function(a.evaluate(vars))

    def __repr__(self):
        return f"<{self.__debug_f}({self.__debug_a})>"


class BinaryExpression(Expression):
    def __init__(
        self,
        function: OperatorType,
        a: Expression,
        b: Expression,
    ):
        self.__debug_f = function.__name__
        self.__debug_a = repr(a)
        self.__debug_b = repr(b)

        self._evaluator = lambda vars: function(a.evaluate(vars), b.evaluate(vars))

    def __repr__(self):
        return f"<{self.__debug_a} {self.__debug_f} {self.__debug_b}>"
