from collections.abc import Callable
from typing import overload

import remedapy as R
from remedapy.decorator import make_data_last


@overload
def to_camel_case(s: str, /, *, preserve_consecutive_uppercase: bool = True) -> str: ...


@overload
def to_camel_case(*, preserve_consecutive_uppercase: bool = True) -> Callable[[str], str]: ...


@make_data_last
def to_camel_case(s: str, /, *, preserve_consecutive_uppercase: bool = True) -> str:
    """
    Makes the string camel case - no spaces, first letter lowercase, next words capitalised.

    Parameters
    ----------
    s : str
        String to camel case (positional-only).
    preserve_consecutive_uppercase: bool
        Whether to not change words made of consecutive uppercase letters. Default: True.

    Returns
    -------
    str
        Camel cased string.

    Examples
    --------
    Data first:
    >>> R.to_camel_case('hello world')
    'helloWorld'
    >>> R.to_camel_case('__HELLO_WORLD__')
    'helloWorld'
    >>> R.to_camel_case('HasHTML')
    'hasHTML'
    >>> R.to_camel_case('HasHTML', preserve_consecutive_uppercase=False)
    'hasHtml'

    Data last:
    >>> R.to_camel_case()('hello world')
    'helloWorld'
    >>> R.to_camel_case()('__HELLO_WORLD__')
    'helloWorld'
    >>> R.to_camel_case()('HasHTML')
    'hasHTML'
    >>> R.to_camel_case(preserve_consecutive_uppercase=False)('HasHTML')
    'hasHtml'

    """
    return R.pipe(
        s,
        R.when(lambda x: all(not x.islower() for x in x), R.to_lower_case),
        R.to_words,
        R.when(lambda: not preserve_consecutive_uppercase, R.map(R.to_lower_case)),
        R.map(R.capitalise),
        R.join(''),
        R.uncapitalise,
    )
