import datetime, numpy as np
from dateutil.relativedelta import relativedelta
from typing_extensions import overload, Self, TypeVar

# Types
_DateT = TypeVar("_DateT", bound=datetime.date)
_DatetimeT = TypeVar("_DatetimeT", bound=datetime.datetime)
_DeltaT = TypeVar("_DeltaT", bound=datetime.timedelta | Delta | relativedelta)
_NumericT = TypeVar("_NumericT", bound=int | float)

# Delta
class Delta:
    def __init__(
        self,
        years: int = 0,
        quarters: int = 0,
        months: int = 0,
        days: int = 0,
        weeks: int = 0,
        hours: int = 0,
        minutes: int = 0,
        seconds: int = 0,
        milliseconds: int = 0,
        microseconds: int = 0,
        year: int = -1,
        month: int = -1,
        day: int = -1,
        weekday: int = -1,
        hour: int = -1,
        minute: int = -1,
        second: int = -1,
        millisecond: int = -1,
        microsecond: int = -1,
    ) -> None: ...
    # Property: relative delta
    @property
    def years(self) -> int: ...
    @property
    def months(self) -> int: ...
    @property
    def days(self) -> int: ...
    @property
    def hours(self) -> int: ...
    @property
    def minutes(self) -> int: ...
    @property
    def seconds(self) -> int: ...
    @property
    def microseconds(self) -> int: ...
    # Properties: absolute delta
    @property
    def year(self) -> int | None: ...
    @property
    def month(self) -> int | None: ...
    @property
    def day(self) -> int | None: ...
    @property
    def weekday(self) -> int | None: ...
    @property
    def hour(self) -> int | None: ...
    @property
    def minute(self) -> int | None: ...
    @property
    def second(self) -> int | None: ...
    @property
    def microsecond(self) -> int | None: ...
    # Arithmetic: addition
    @overload
    def __add__(self, o: _DatetimeT) -> _DatetimeT: ...
    @overload
    def __add__(self, o: _DateT) -> _DateT: ...
    @overload
    def __add__(self, o: _DeltaT) -> Self: ...
    @overload
    def __add__(self, o: np.datetime64) -> datetime.datetime: ...
    @overload
    def __add__(self, o: np.timedelta64) -> Self: ...
    @overload
    def __add__(self, o: _NumericT) -> Self: ...
    # Arithmetic: right addition
    @overload
    def __radd__(self, o: _DatetimeT) -> _DatetimeT: ...
    @overload
    def __radd__(self, o: _DateT) -> _DateT: ...
    @overload
    def __radd__(self, o: _DeltaT) -> Self: ...
    @overload
    def __radd__(self, o: _NumericT) -> Self: ...
    # Arithmetic: subtraction
    @overload
    def __sub__(self, o: _DeltaT) -> Self: ...
    @overload
    def __sub__(self, o: np.timedelta64) -> Self: ...
    @overload
    def __sub__(self, o: _NumericT) -> Self: ...
    # Arithmetic: right subtraction
    @overload
    def __rsub__(self, o: _DatetimeT) -> _DatetimeT: ...
    @overload
    def __rsub__(self, o: _DateT) -> _DateT: ...
    @overload
    def __rsub__(self, o: _DeltaT) -> Self: ...
    @overload
    def __rsub__(self, o: _NumericT) -> Self: ...
    # Arithmetic: multiplication
    @overload
    def __mul__(self, o: _NumericT) -> Self: ...
    @overload
    def __rmul__(self, o: _NumericT) -> Self: ...
    # Arithmetic: division
    @overload
    def __truediv__(self, o: _NumericT) -> Self: ...
    # Arithmetic: negation
    def __neg__(self) -> Self: ...
    # Arithmetic: absolute
    def __abs__(self) -> Self: ...
    # Comparison
    def __eq__(self, o: object) -> bool: ...
    def __bool__(self) -> bool: ...
    # Representation
    def __repr__(self) -> str: ...
    def __hash__(self) -> int: ...
