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

from remedapy.decorator import make_data_last
from remedapy.util import NonPositiveInt

TNum = TypeVar('TNum', int, float)


@overload
def ceil(precision: NonPositiveInt, /) -> Callable[[int | float], int]: ...


@overload
def ceil(precision: int, /) -> Callable[[TNum], TNum]: ...


@overload
def ceil(value: int | float, precision: NonPositiveInt, /) -> int: ...


@overload
def ceil(value: TNum, precision: int, /) -> TNum: ...


@make_data_last
def ceil(
    value: int | float,
    precision: int,
    /,
) -> int | float:
    """
    Rounds up a given number to a specific precision.

    Parameters
    ----------
    value : int | float
        Number to round up (positional-only).
    precision : int
        Desired precision (positional-only).

    Returns
    -------
    int | float
        rounded value, int if precision is non-positive, otherwise float.

    Examples
    --------
    Data first:
    >>> R.ceil(123.9876, 3)
    123.988
    >>> R.ceil(8541.1, -1)
    8550

    Data last:
    >>> R.ceil(1)(483.22243)
    483.3
    >>> R.ceil(-3)(456789)
    457000

    """
    if precision == 0:
        return math.ceil(value)
    factor = 10**precision
    result = math.ceil(value * factor) / factor
    if precision > 0:
        return result
    return int(result)
