# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_util.ipynb.

# %% auto 0
__all__ = ['safeattr', 'sens', 'strmatch', 'keymatch']

# %% ../nbs/01_util.ipynb 4
# from pstr import safeattr, keymatch
import re, difflib, string
from types import ModuleType
from typing import Any, Self, Optional, ParamSpec, TypedDict

# %% ../nbs/01_util.ipynb 5
from .cons import P, U1, NIL, ATTR, PUNC, SensitivityKeywords, StringMatchKeywords

# %% ../nbs/01_util.ipynb 7
def safeattr(name: str) -> str:
    return re.sub(ATTR, NIL, re.sub(PUNC, U1, name))

# %% ../nbs/01_util.ipynb 8
def sens(
    s: str, 
    icase: bool = True, 
    ispace: bool = True, 
    iunder: bool = True, 
    **kwargs: P.kwargs
) -> str:
    '''Prepare a string by applying case and space insensitivity rules.
    
    Parameters
    ----------
    icase : bool, optional
        Whether to ignore case when matching, by default True.
    ispace : bool, optional
        Whether to ignore spaces when matching, by default True.
    iunder : bool, optional
        Whether to ignore underscores when matching, by default True.    
    '''
    if icase:  s = s.lower()
    if ispace: s = re.sub(r'\s', '', s)
    if iunder: s = re.sub(r'_', '', s)        
    return s

# %% ../nbs/01_util.ipynb 9
def strmatch(
    a: str, 
    b: str, 
    n: int = 1, 
    cutoff: float = 0.8,
    icase: bool = True, 
    ispace: bool = False, 
    iunder: bool = False,
) -> Optional[str]:
    '''
    Match string `a` against candidate `b` after normalizing both strings.

    The matching is done using difflib's get_close_matches to find close matches to the string `a` 
    in a sequence containing only the string `b`. String normalization can be controlled via 
    boolean flags which remove spaces, underscores, and set characters to lowercase.

    Parameters
    ----------
    a : str
        The string to match against.
    b : str
        The candidate string to match with.
    n : int, optional
        The maximum number of close matches to return; here, it is either 0 or 1, by default 1.
    cutoff : float, optional
        The similarity ratio (0 <= ratio <= 1) below which matches are not considered, by default 0.8.
    icase : bool, optional
        If True, ignores case during comparison, by default True.
    ispace : bool, optional
        If True, ignores spaces during comparison, by default False.
    iunder : bool, optional
        If True, ignores underscores during comparison, by default False.

    Returns
    -------
    str or None
        The candidate `b` if it is considered a match; otherwise, None.
    '''
    a = sens(a, icase, ispace, iunder)
    c = sens(b[:], icase, ispace, iunder)
    same = difflib.get_close_matches(a, (c, ), n, cutoff)
    if same: return b
    return None

# %% ../nbs/01_util.ipynb 10
def keymatch(
    s: str, 
    key: str, 
    val: str = None,
    n: int = 1, 
    cutoff: float = 0.8,
    icase: bool = True, 
    ispace: bool = False, 
    iunder: bool = False,
) -> Optional[str]:
    '''
    Match a string `s` against a `key` and optionally a secondary string `val` after normalization.

    Attempts to match the string `s` with `key` first; if that fails and `val` is provided,
    it attempts to match with `val`. Matching uses `strmatch` internally, applying the same
    normalization and similarity checking.

    Parameters
    ----------
    s : str
        The string to be matched.
    key : str
        The primary key to match against.
    val : str, optional
        An additional value to attempt to match `s` against if the match with `key` fails, by default None.
    n : int, optional
        The maximum number of close matches to return; here, it is either 0 or 1, by default 1.
    cutoff : float, optional
        The similarity ratio (0 <= ratio <= 1) below which matches are not considered, by default 0.8.
    icase : bool, optional
        If True, comparison ignores case, by default True.
    ispace : bool, optional
        If True, comparison ignores spaces, by default False.
    iunder : bool, optional
        If True, comparison ignores underscores, by default False.

    Returns
    -------
    str or None
        The `key` if a match with `s` is found; otherwise, None.

    '''
    kwds = dict(n=n, cutoff=cutoff, icase=icase, ispace=ispace, iunder=iunder)
    same = strmatch(s, key, **kwds)
    if same is None and val is not None:
        same = strmatch(s, val, **kwds)
    if same: return key
    return None
