"""minos.common.queries module."""
from __future__ import (
    annotations,
)
from abc import (
    ABC,
    abstractmethod,
)
from functools import (
    partial,
)
from operator import (
    attrgetter,
)
from typing import (
    Any,
    Callable,
    Iterable,
)
from minos.common import (
    Model,
)
class _Condition(ABC):
    def evaluate(self, value: Model) -> bool:
        """Evaluate if given value satisfied this condition.
        :param value: The value to be evaluated.
        :return: A boolean value.
        """
        return self._evaluate(value)
    @abstractmethod
    def _evaluate(self, value: Model) -> bool:
        pass
    def __eq__(self, other) -> bool:
        return type(self) == type(other) and tuple(self) == tuple(other)
    def __hash__(self) -> int:
        return hash(tuple(self))
    def __iter__(self) -> Iterable[Any]:
        yield from tuple()
    def __repr__(self) -> str:
        return f"{type(self).__name__}({', '.join(map(repr, self))})"
class _TrueCondition(_Condition):
    def _evaluate(self, value: Model) -> bool:
        return True
class _FalseCondition(_Condition):
    def _evaluate(self, value: Model) -> bool:
        return False
class _ComposedCondition(_Condition, ABC):
    def __init__(self, *parts: _Condition):
        self.parts = tuple(parts)
    def __iter__(self):
        yield from self.parts
class _AndCondition(_ComposedCondition):
    def _evaluate(self, value: Model) -> bool:
        return all(c.evaluate(value) for c in self.parts)
class _OrCondition(_ComposedCondition):
    def _evaluate(self, value: Model) -> bool:
        return any(c.evaluate(value) for c in self.parts)
class _NotCondition(_Condition):
    def __init__(self, inner: _Condition):
        self.inner = inner
    def _evaluate(self, value: Model) -> bool:
        return not self.inner.evaluate(value)
    def __iter__(self) -> Iterable[Any]:
        yield from (self.inner,)
class _SimpleCondition(_Condition, ABC):
    def __init__(self, field: str, parameter: Any):
        self.field = field
        self.parameter = parameter
    def __iter__(self) -> Iterable[Any]:
        yield from (
            self.field,
            self.parameter,
        )
    @property
    def _get_field(self) -> Callable[[Any], Any]:
        return attrgetter(self.field)
class _LowerCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) < self.parameter
class _LowerEqualCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) <= self.parameter
class _GreaterCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) > self.parameter
class _GreaterEqualCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) >= self.parameter
class _EqualCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) == self.parameter
class _NotEqualCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) != self.parameter
class _InCondition(_SimpleCondition):
    def _evaluate(self, value: Model) -> bool:
        return self._get_field(value) in self.parameter
_TRUE_CONDITION = _TrueCondition()
_FALSE_CONDITION = _FalseCondition()
[docs]class Condition:
    """Condition class.
    This class provides the way to create filtering conditions for ``Model`` instances based on the following operators:
    * `TRUE`: Always evaluates as `True`.
    * `FALSE`: Always evaluates as `True`.
    * `AND`: Only evaluates as `True` if all the given conditions are evaluated as `True`.
    * `OR`: Evaluates as `True` if at least one of the given conditions are evaluated as `True`.
    * `NOT`: Evaluates as `True` only if the inner condition is evaluated as `False`.
    * `LOWER`: Evaluates as `True` only if the field of the given model is lower (<) than the parameter.
    * `LOWER_EQUAL`: Evaluates as `True` only if the field of the given model is lower or equal (<=) to the parameter.
    * `GREATER`: Evaluates as `True` only if the field of the given model is greater (>) than the parameter.
    * `GREATER_EQUAL`: Evaluates as `True` only if the field of the given model is greater or equal (>=) to the
    parameter.
    * `EQUAL`: Evaluates as `True` only if the field of the given model is equal (==) to the parameter.
    * `NOT_EQUAL`: Evaluates as `True` only if the field of the given model is not equal (!=) to the parameter.
    * `IN`: Evaluates as `True` only if the field of the given model belongs (in) to the parameter (which must be a
    collection).
    For example, to define a condition in which the `year` must be between `1994` and `2003` or the `color` must be
    `blue`, the condition can be writen as:
    .. code-block::
        Condition.OR(
            Condition.AND(Condition.GREATER_EQUAL("year", 1994), Condition.LOWER("year", 2003)),
            Condition.EQUAL("color", "blue")
        )
    """
    TRUE = _TRUE_CONDITION
    FALSE = _FALSE_CONDITION
    AND = _AndCondition
    OR = _OrCondition
    NOT = _NotCondition
    LOWER = _LowerCondition
    LOWER_EQUAL = _LowerEqualCondition
    GREATER = _GreaterCondition
    GREATER_EQUAL = _GreaterEqualCondition
    EQUAL = _EqualCondition
    NOT_EQUAL = _NotEqualCondition
    IN = _InCondition 
class _Ordering:
    def __init__(self, by: str, reverse: bool):
        self.by = by
        self.reverse = reverse
    def __eq__(self, other) -> bool:
        return type(self) == type(other) and tuple(self) == tuple(other)
    def __hash__(self) -> int:
        return hash(tuple(self))
    def __iter__(self) -> Iterable[Any]:
        yield from (
            self.by,
            self.reverse,
        )
    def __repr__(self) -> str:
        return f"{type(self).__name__}({', '.join(map(repr, self))})"
[docs]class Ordering:
    """Ordering class.
    This class provides the way to define ordering strategies for ``Model`` instances through the ``ASC`` and ``DESC``
    class methods, which retrieves instances containing the given information.
    For example, to define a descending ordering strategy by the `name` field:
    .. code-block::
        Ordering.DESC("name")
    """
    ASC = partial(_Ordering, reverse=False)
    DESC = partial(_Ordering, reverse=True)