from __future__ import annotations

import dataclasses
import enum
from abc import abstractmethod
from typing import Generic, Protocol, TypeVar


class Comparable(Protocol):
    @abstractmethod
    def __gt__(self: T, other: T, /) -> bool:
        pass

    @abstractmethod
    def __ge__(self: T, other: T, /) -> bool:
        pass

    @abstractmethod
    def __lt__(self: T, other: T, /) -> bool:
        pass

    @abstractmethod
    def __le__(self: T, other: T, /) -> bool:
        pass


T = TypeVar("T", bound=Comparable)


@dataclasses.dataclass
class Range(Generic[T]):
    gt: T | None = None
    gte: T | None = None
    lt: T | None = None
    lte: T | None = None

    def lower_bound(self) -> tuple[T, bool] | None:
        if self.gt is None and self.gte is None:
            return None
        elif self.gt is not None and self.gte is not None:
            lb = self.gt if (self.gt > self.gte) else self.gte
            eq = self.gt < self.gte
            return lb, eq
        elif self.gt is not None:
            return self.gt, False
        elif self.gte is not None:
            return self.gte, True

    def upper_bound(self) -> tuple[T, bool] | None:
        if self.lt is None and self.lte is None:
            return None
        elif self.lt is not None and self.lte is not None:
            ub = self.lt if (self.lt < self.lte) else self.lte
            eq = self.lt > self.lte
            return ub, eq
        elif self.lt is not None:
            return self.lt, False
        elif self.lte is not None:
            return self.lte, True


class Order(enum.Enum):
    ASCENDING = 'ASC'
    DESCENDING = 'DESC'


ColT = TypeVar("ColT")


@dataclasses.dataclass
class OrderCol(Generic[ColT]):
    column: ColT
    order: Order
