import re
from collections.abc import Callable
from typing import Final, overload

from remedapy.decorator import make_data_last

WHITESPACE: Final = {
    '\u0009',  # '\t'
    '\u000a',  # '\n'
    '\u000b',  # '\v'
    '\u000c',  # '\f'
    '\u000d',  # '\r'
    '\u0020',  # ' '
    '\u0085',
    '\u00a0',
    '\u1680',
    '\u2000',
    '\u2001',
    '\u2002',
    '\u2003',
    '\u2004',
    '\u2005',
    '\u2006',
    '\u2007',
    '\u2008',
    '\u2009',
    '\u200a',
    '\u2028',
    '\u2029',
    '\u202f',
    '\u205f',
    '\u3000',
    '\ufeff',
}

WORD_SEPARATORS: Final = {'-', '_'} | WHITESPACE


def _words(data: str) -> list[str]:
    results: list[str] = []
    word: str = ''

    def flush() -> None:
        nonlocal word
        if word:
            results.append(word)
            word = ''

    for ch in data:
        if ch in WORD_SEPARATORS:
            flush()
            continue
        if not word:
            word = ch
            continue
        # 1) lowercase to uppercase cuteCat -> ["cute", "Cat"]
        if re.search(r'[a-z]$', word) and re.search(r'[A-Z]', ch):
            flush()
        # 2) uppercase to lowercase following multiple uppercase letters e.g. BIGDiff -> ["BIG", "Diff"]
        elif re.search(r'[A-Z]{2}$', word) and re.search(r'[a-z]', ch):
            last_char = word[-1]
            word = word[:-1]
            flush()
            word = last_char
        # 3) digit boundary
        elif word[-1].isdigit() != ch.isdigit():
            flush()

        word += ch

    flush()

    return results


@overload
def to_words(s: str, /) -> list[str]: ...


@overload
def to_words() -> Callable[[str], list[str]]: ...


@make_data_last
def to_words(s: str, /) -> list[str]:
    """
    Split sting into words.

    Parameters
    ----------
    s : str
        String to split (positional-only).

    Returns
    -------
    list[str]
        List of words.

    Examples
    --------
    Data first:
    >>> R.to_words('is_string-CamelCase')
    ['is', 'string', 'Camel', 'Case']

    Data last:
    >>> R.to_words()('is_string-CamelCase')
    ['is', 'string', 'Camel', 'Case']

    """
    return _words(s)
