# pyright: strict
from collections.abc import (
    Callable,
    Sequence,
)
import re
from typing import (
    Any,
    Generic,
    Literal,
    TypeVar,
    overload,
)

import numpy as np
import numpy.typing as npt
import pandas as pd
from pandas import (
    DataFrame,
    Index,
    MultiIndex,
    Series,
)
from pandas.core.base import NoNewAttributesMixin

from pandas._libs.tslibs.nattype import NaTType
from pandas._typing import (
    JoinHow,
    Scalar,
    T,
    np_ndarray_bool,
)

# Used for the result of str.split with expand=True
_T_EXPANDING = TypeVar("_T_EXPANDING", bound=DataFrame | MultiIndex)
# Used for the result of str.split with expand=False
_T_LIST_STR = TypeVar("_T_LIST_STR", bound=Series[list[str]] | Index[list[str]])
# Used for the result of str.match
_T_BOOL = TypeVar("_T_BOOL", bound=Series[bool] | np_ndarray_bool)
# Used for the result of str.index / str.find
_T_INT = TypeVar("_T_INT", bound=Series[int] | Index[int])
# Used for the result of str.encode
_T_BYTES = TypeVar("_T_BYTES", bound=Series[bytes] | Index[bytes])
# Used for the result of str.decode
_T_STR = TypeVar("_T_STR", bound=Series[str] | Index[str])
# Used for the result of str.partition
_T_OBJECT = TypeVar("_T_OBJECT", bound=Series[type[object]] | Index[type[object]])

class StringMethods(
    NoNewAttributesMixin,
    Generic[T, _T_EXPANDING, _T_BOOL, _T_LIST_STR, _T_INT, _T_BYTES, _T_STR, _T_OBJECT],
):
    def __init__(self, data: T) -> None: ...
    def __getitem__(self, key: slice | int) -> _T_STR: ...
    def __iter__(self) -> _T_STR: ...
    @overload
    def cat(
        self,
        *,
        sep: str,
        na_rep: str | None = ...,
        join: JoinHow = ...,
    ) -> str: ...
    @overload
    def cat(
        self,
        others: Literal[None] = ...,
        *,
        sep: str,
        na_rep: str | None = ...,
        join: JoinHow = ...,
    ) -> str: ...
    @overload
    def cat(
        self,
        others: (
            Series[str] | Index[str] | pd.DataFrame | npt.NDArray[np.str_] | list[str]
        ),
        sep: str = ...,
        na_rep: str | None = ...,
        join: JoinHow = ...,
    ) -> _T_STR: ...
    @overload
    def split(
        self, pat: str = ..., *, n: int = ..., expand: Literal[True], regex: bool = ...
    ) -> _T_EXPANDING: ...
    @overload
    def split(
        self,
        pat: str = ...,
        *,
        n: int = ...,
        expand: Literal[False] = ...,
        regex: bool = ...,
    ) -> _T_LIST_STR: ...
    @overload
    def rsplit(
        self, pat: str = ..., *, n: int = ..., expand: Literal[True]
    ) -> _T_EXPANDING: ...
    @overload
    def rsplit(
        self, pat: str = ..., *, n: int = ..., expand: Literal[False] = ...
    ) -> _T_LIST_STR: ...
    @overload
    def partition(self, sep: str = ...) -> _T_EXPANDING: ...
    @overload
    def partition(self, *, expand: Literal[True]) -> _T_EXPANDING: ...
    @overload
    def partition(self, sep: str, expand: Literal[True]) -> _T_EXPANDING: ...
    @overload
    def partition(self, sep: str, expand: Literal[False]) -> _T_OBJECT: ...
    @overload
    def partition(self, *, expand: Literal[False]) -> _T_OBJECT: ...
    @overload
    def rpartition(self, sep: str = ...) -> _T_EXPANDING: ...
    @overload
    def rpartition(self, *, expand: Literal[True]) -> _T_EXPANDING: ...
    @overload
    def rpartition(self, sep: str, expand: Literal[True]) -> _T_EXPANDING: ...
    @overload
    def rpartition(self, sep: str, expand: Literal[False]) -> _T_OBJECT: ...
    @overload
    def rpartition(self, *, expand: Literal[False]) -> _T_OBJECT: ...
    def get(self, i: int) -> _T_STR: ...
    def join(self, sep: str) -> _T_STR: ...
    def contains(
        self,
        pat: str | re.Pattern[str],
        case: bool = ...,
        flags: int = ...,
        na: Scalar | NaTType | None = ...,
        regex: bool = ...,
    ) -> _T_BOOL: ...
    def match(
        self, pat: str, case: bool = ..., flags: int = ..., na: Any = ...
    ) -> _T_BOOL: ...
    def replace(
        self,
        pat: str,
        repl: str | Callable[[re.Match[str]], str],
        n: int = ...,
        case: bool | None = ...,
        flags: int = ...,
        regex: bool = ...,
    ) -> _T_STR: ...
    def repeat(self, repeats: int | Sequence[int]) -> _T_STR: ...
    def pad(
        self,
        width: int,
        side: Literal["left", "right", "both"] = ...,
        fillchar: str = ...,
    ) -> _T_STR: ...
    def center(self, width: int, fillchar: str = ...) -> _T_STR: ...
    def ljust(self, width: int, fillchar: str = ...) -> _T_STR: ...
    def rjust(self, width: int, fillchar: str = ...) -> _T_STR: ...
    def zfill(self, width: int) -> _T_STR: ...
    def slice(
        self, start: int | None = ..., stop: int | None = ..., step: int | None = ...
    ) -> T: ...
    def slice_replace(
        self, start: int | None = ..., stop: int | None = ..., repl: str | None = ...
    ) -> _T_STR: ...
    def decode(self, encoding: str, errors: str = ...) -> _T_STR: ...
    def encode(self, encoding: str, errors: str = ...) -> _T_BYTES: ...
    def strip(self, to_strip: str | None = ...) -> _T_STR: ...
    def lstrip(self, to_strip: str | None = ...) -> _T_STR: ...
    def rstrip(self, to_strip: str | None = ...) -> _T_STR: ...
    def wrap(
        self,
        width: int,
        expand_tabs: bool | None = ...,
        replace_whitespace: bool | None = ...,
        drop_whitespace: bool | None = ...,
        break_long_words: bool | None = ...,
        break_on_hyphens: bool | None = ...,
    ) -> _T_STR: ...
    def get_dummies(self, sep: str = ...) -> _T_EXPANDING: ...
    def translate(self, table: dict[int, int | str | None] | None) -> _T_STR: ...
    def count(self, pat: str, flags: int = ...) -> _T_INT: ...
    def startswith(self, pat: str | tuple[str, ...], na: Any = ...) -> _T_BOOL: ...
    def endswith(self, pat: str | tuple[str, ...], na: Any = ...) -> _T_BOOL: ...
    def findall(self, pat: str, flags: int = ...) -> _T_LIST_STR: ...
    @overload
    def extract(
        self, pat: str, flags: int = ..., *, expand: Literal[True] = ...
    ) -> pd.DataFrame: ...
    @overload
    def extract(self, pat: str, flags: int, expand: Literal[False]) -> _T_OBJECT: ...
    @overload
    def extract(
        self, pat: str, flags: int = ..., *, expand: Literal[False]
    ) -> _T_OBJECT: ...
    def extractall(self, pat: str, flags: int = ...) -> pd.DataFrame: ...
    def find(self, sub: str, start: int = ..., end: int | None = ...) -> _T_INT: ...
    def rfind(self, sub: str, start: int = ..., end: int | None = ...) -> _T_INT: ...
    def normalize(self, form: Literal["NFC", "NFKC", "NFD", "NFKD"]) -> _T_STR: ...
    def index(self, sub: str, start: int = ..., end: int | None = ...) -> _T_INT: ...
    def rindex(self, sub: str, start: int = ..., end: int | None = ...) -> _T_INT: ...
    def len(self) -> _T_INT: ...
    def lower(self) -> _T_STR: ...
    def upper(self) -> _T_STR: ...
    def title(self) -> _T_STR: ...
    def capitalize(self) -> _T_STR: ...
    def swapcase(self) -> _T_STR: ...
    def casefold(self) -> _T_STR: ...
    def isalnum(self) -> _T_BOOL: ...
    def isalpha(self) -> _T_BOOL: ...
    def isdigit(self) -> _T_BOOL: ...
    def isspace(self) -> _T_BOOL: ...
    def islower(self) -> _T_BOOL: ...
    def isupper(self) -> _T_BOOL: ...
    def istitle(self) -> _T_BOOL: ...
    def isnumeric(self) -> _T_BOOL: ...
    def isdecimal(self) -> _T_BOOL: ...
    def fullmatch(
        self, pat: str, case: bool = ..., flags: int = ..., na: Any = ...
    ) -> _T_BOOL: ...
    def removeprefix(self, prefix: str) -> _T_STR: ...
    def removesuffix(self, suffix: str) -> _T_STR: ...
