import re
from collections.abc import Callable, Iterable

from .fp import endofilter
from .typing import areinstances


def as_pattern(s: str | re.Pattern[str]) -> re.Pattern[str]:
    return re.compile(s) if isinstance(s, str) else s


def re_bool_search(p: re.Pattern[str], s: str | None) -> bool:
    if not s:
        return False
    return bool(re.search(p, s))


def contains_re(pattern: re.Pattern[str], s: str | Iterable[str]) -> bool:
    if isinstance(s, str):
        return bool(re.search(pattern, s))
    for element in s:
        if re.search(pattern, element):
            return True
    return False


def re_group1(p: re.Pattern[str] | str, s: str | None) -> str | None:
    if s is None:
        return None
    if result := re.search(p, s):
        try:
            return result.group(1)
        except IndexError:
            return None
    return None


def re_groupdict(p: re.Pattern[str] | str, s: str | None) -> dict[str, str] | None:
    if s is None:
        return None
    if result := re.search(p, s):
        return result.groupdict()
    return None


def make_search(p: str | re.Pattern[str]) -> Callable[[str], bool]:
    if isinstance(p, str):
        p = re.compile(p)

    def search(s: str) -> bool:
        return bool(re.search(p, s))

    return search


def re_filter[T: list[str] | tuple[str, ...] | set[str]](p: str | re.Pattern[str], seq: T) -> T:
    return endofilter(make_search(p), seq)  # type: ignore


def re_split(expr: str | re.Pattern[str], s: str | None, maxsplit: int | None = None) -> list[str]:
    if s is None:
        return []
    segments = re.split(expr, s, maxsplit=maxsplit or 0)
    if not areinstances(segments, str):
        raise TypeError(
            "Function 're_split' may only return a list of strings."
        )  # pragma: no cover
    return segments


class Patterns:
    DATE: re.Pattern[str] = re.compile(r"^[12]\d\d\d-(0?[1-9]|1[012]|)-(0?[1-9]|[12]\d|3[01])$")
    DATE_STRICT: re.Pattern[str] = re.compile(
        r"^[12]\d\d\d-(0[1-9]|1[012]|)-(0[1-9]|[12]\d|3[01])$"
    )
    DATE_LOOSE: re.Pattern[str] = re.compile(r"(\d{2,4})[^\d](\d\d?)[^\d](\d\d?)")
    ID: re.Pattern[str] = re.compile(r"^[A-Za-z][A-Za-z0-9_]*$")
    IDS: re.Pattern[str] = re.compile(r"^([A-Za-z][A-Za-z0-9_]*)(,[A-Za-z][A-Za-z0-9_]*)*$")
    NATURAL: re.Pattern[str] = re.compile(r"^[1-9][0-9]*$")
    PROPORTION: re.Pattern[str] = re.compile(r"^0?\.[0-9]+$")
    PROPORTION_OR_2: re.Pattern[str] = re.compile(r"^0?\.[0-9]+$|^0?\.[0-9]+,0?\.[0-9]+$")
