from collections.abc import Callable
from typing import TypeVar, overload

T = TypeVar('T')


@overload
def negate(fn: Callable[[T], bool], /) -> Callable[[T], bool]: ...


@overload
def negate(data: T, fn: Callable[[T], bool], /) -> bool: ...


@overload
def negate(data: bool, /) -> bool: ...


@overload
def negate() -> Callable[[bool], bool]: ...


def negate(data: T | Callable[[T], bool] | None = None, predicate: Callable[[T], bool] | None = None, /):  # pyright: ignore
    """
    Given a value and a predicate returns the opposite of the predicate applied to the value.

    Can be given only a value.

    Parameters
    ----------
    data: T
        Data to pass to predicate.
    predicate:
        Predicate to check data on.

    Returns
    -------
    boolean
        Whether the data doesn't satisfy the predicate.

    Examples
    --------
    Data first:
    >>> R.negate(0, R.is_truthy)
    True
    >>> R.negate([1, 2, 3], R.is_truthy)
    False
    >>> R.negate(False)
    True

    Data last:
    >>> R.negate(R.is_truthy)(0)
    True
    >>> R.negate()(True)
    False

    """
    if predicate is None:
        if data is None:
            return lambda x: not x  # pyright: ignore
        elif isinstance(data, Callable):
            return lambda x: not data(x)  # pyright: ignore
        else:
            return not data
    return not predicate(data)  # pyright: ignore
