import re
import sys
from functools import partial
from typing import Any, Callable, Iterator, Self

from mypy_extensions import DefaultNamedArg, VarArg

from .base.interface import InputType

type EZRegexDefinition = str|Callable[[VarArg, DefaultNamedArg[str, "cur"]], str]|list[partial[str]]

class EZRegex:
    """ Represent parts of the Regex syntax. Should not be instantiated by the user directly."""
    _sanatize: bool
    _flags: str
    # _funcList: list[Callable[[VarArg, DefaultNamedArg[str, "cur"]], str]]
    _funcList: list
    replacement: bool

    def __init__(self, definition:EZRegexDefinition, *, sanatize:bool=True, replacement:bool=False, flags:str='') -> None:
        """ The workhorse of the EZRegex library. This represents a regex pattern that can be combined
            with other EZRegexs and strings. Ideally, this should only be called internally, but it should
            still work from the user's end
        """

    # Private functions
    def _flag_func(self, final:str) -> str: ...
    def _escape(self, pattern:str) -> str:
        """ This function was modified from the one in /usr/lib64/python3.12/re/__init__.py line 255 """
    def _sanitizeInput(self, i:InputType, addFlags:bool=False) -> str:
        """ Instead of rasising an error if passed a strange datatype, it now trys to cast it to a string """
    def _compile(self, addFlags=True) -> str: ...
    def _copy(self, definition:EZRegexDefinition=..., sanatize:bool=..., replacement:bool=..., flags:str=...): ...

    # Regular functions
    def compile(self, addFlags=True) -> re.Pattern: ...
    def str(self) -> str: ...
    def debug(self) -> EZRegex: ...
    def copy(self, addFlags=True) -> None: ...
    def test(self, testString:str=None, show=True, context=True) -> bool:
        """ Tests the current regex expression to see if it's in @param testString.
            Returns the match objects (None if there was no match)
        """
    def invert(self, amt=1, **kwargs) -> str: ...
    def inverse(self, amt=1, **kwargs) -> str:
        """ "Inverts" the current Regex expression to give an example of a string it would match.
            Useful for debugging purposes. """

    # Elemental functions
    def group(self, name:str=None) -> EZRegex: ...
    def named(self, name:str) -> EZRegex: ...
    @property
    def unnamed(self) -> EZRegex: ...
    def if_not_preceded_by(self, input:InputType) -> EZRegex: ...
    def if_preceded_by(self, input:InputType) -> EZRegex: ...
    def if_not_proceded_by(self, input:InputType) -> EZRegex: ...
    def if_proceded_by(self, input:InputType) -> EZRegex: ...
    def if_not_followed_by(self, input:InputType) -> EZRegex: ...
    def if_followed_by(self, input:InputType) -> EZRegex: ...
    def if_enclosed_with(self, open:str, closed:str|None=None) -> EZRegex: ...
    @property
    def optional(self) -> EZRegex: ...
    @property
    def repeat(self) -> EZRegex: ...
    @property
    def exactly(self) -> EZRegex: ...
    def at_least(self, min:int) -> EZRegex: ...
    def more_than(self, min:int) -> EZRegex: ...
    def amt(self, amt:int) -> EZRegex: ...
    def at_most(self, max:int) -> EZRegex: ...
    def between(self, min:int, max:int, greedy:bool=True, possessive:bool=False) -> EZRegex: ...
    def at_least_one(self, greedy:bool=True, possessive:bool=False) -> EZRegex: ...
    def at_least_none(self, greedy:bool=True, possessive:bool=False) -> EZRegex: ...
    def or_(self, input:InputType) -> EZRegex: ...
    @property
    def ASCII(self) -> EZRegex: ...
    @property
    def IGNORECASE(self) -> EZRegex: ...
    @property
    def DOTALL(self) -> EZRegex: ...
    @property
    def LOCALE(self) -> EZRegex: ...
    @property
    def MULTILINE(self) -> EZRegex: ...
    @property
    def UNICODE(self) -> EZRegex: ...

    # Named operator functions
    def append(self, input:InputType) -> EZRegex: ...
    def prepend(self, input:InputType) -> EZRegex: ...

    # Flag functions
    @property
    def flags(self) -> str: ...
    def set_flags(self, to:str) -> EZRegex: ...
    def add_flag(self, flag:str) -> EZRegex: ...
    def remove_flag(self, flag:str) -> EZRegex: ...

    # Magic Functions
    def __call__(self, *args, **kwargs) -> EZRegex | str:
        """ This should be called by the user to specify the specific parameters of this instance i.e. anyof('a', 'b') """
    def __str__(self, addFlags:bool=True) -> str: ...
    def __repr__(self) -> str: ...
    def __eq__(self, thing:InputType) -> bool: ...
    def __mul__(self, amt:int) -> EZRegex: ...
    def __rmul__(self, amt:int) -> EZRegex: ...
    def __imul__(self, amt:int) -> EZRegex: ...
    def __add__(self, thing:InputType) -> EZRegex: ...
    def __radd__(self, thing:InputType) -> EZRegex: ...
    def __iadd__(self, thing:InputType) -> EZRegex: ...
    def __and__(self, thing:InputType) -> EZRegex: ...
    def __rand__(self, thing:InputType) -> EZRegex: ...
    # The shift operators just shadow the add operators
    def __lshift__(self, thing:InputType) -> EZRegex: ...
    def __rlshift__(self, thing:InputType) -> EZRegex: ...
    def __ilshift__(self, thing:InputType) -> EZRegex: ...
    # I don't think right and left shifts should be any different, right?
    def __rshift__(self, thing:InputType) -> EZRegex: ...
    def __rrshift__(self, thing:InputType) -> EZRegex: ...
    def __irshift__(self, thing:InputType) -> EZRegex: ...
    def __invert__(self) -> str: ...
    def __pos__(self) -> EZRegex:
        """ TODO: Add documentation here """
    def __ror__(self, thing:InputType) -> EZRegex:
        """ TODO: Add documentation here """
    def __or__(self, thing:InputType) -> EZRegex: ...
    def __xor__(self, thing:InputType) -> EZRegex: ...
    def __rxor__(self, thing:InputType) -> EZRegex: ...
    def __mod__(self, other:str) -> re.Match|None:
        """ I would prefer __rmod__(), but it doesn't work on strings, since __mod__() is already specified for string formmating. """
    def __hash__(self) -> int: ...
    def __contains__(self, thing:str) -> bool: ...
    def __getitem__(self, args) -> EZRegex: ...
    def __reversed__(self) -> str: ...
    def __rich__(self) -> str: ...
    def __pretty__(self) -> str: ...
    # To make EZRegex instances immutable
    def __setattr__(self, name:str, value, ignore=False): ...
    def __delattr__(self, *args): ...
