import re
from _io import BytesIO, StringIO
from _typeshed import Incomplete, ReadableBuffer, SupportsRead
from collections.abc import Callable, Iterable
from typing import Any, AnyStr, Generic, Literal, TypeAlias, TypeVar, overload
from typing_extensions import Self

from webencodings import Encoding

_UnicodeInputStream: TypeAlias = str | SupportsRead[str]
_BinaryInputStream: TypeAlias = bytes | SupportsRead[bytes]
_InputStream: TypeAlias = _UnicodeInputStream | _BinaryInputStream  # noqa: Y047  # used in other files
_SupportsReadT = TypeVar("_SupportsReadT", bound=SupportsRead[Any])
_SupportsReadBytesT = TypeVar("_SupportsReadBytesT", bound=SupportsRead[bytes])

spaceCharactersBytes: frozenset[bytes]
asciiLettersBytes: frozenset[bytes]
asciiUppercaseBytes: frozenset[bytes]
spacesAngleBrackets: frozenset[bytes]
invalid_unicode_no_surrogate: str
invalid_unicode_re: re.Pattern[str]
non_bmp_invalid_codepoints: set[int]
ascii_punctuation_re: re.Pattern[str]
charsUntilRegEx: dict[tuple[Iterable[str | bytes | bytearray], bool], re.Pattern[str]]

class BufferedStream(Generic[AnyStr]):
    stream: SupportsRead[AnyStr]
    buffer: list[AnyStr]
    position: list[int]
    def __init__(self, stream: SupportsRead[AnyStr]) -> None: ...
    def tell(self) -> int: ...
    def seek(self, pos: int) -> None: ...
    def read(self, bytes: int) -> AnyStr: ...

@overload
def HTMLInputStream(source: _UnicodeInputStream) -> HTMLUnicodeInputStream: ...
@overload
def HTMLInputStream(
    source: _BinaryInputStream,
    *,
    override_encoding: str | bytes | None = None,
    transport_encoding: str | bytes | None = None,
    same_origin_parent_encoding: str | bytes | None = None,
    likely_encoding: str | bytes | None = None,
    default_encoding: str = "windows-1252",
    useChardet: bool = True,
) -> HTMLBinaryInputStream: ...

class HTMLUnicodeInputStream:
    reportCharacterErrors: Callable[[str], None]
    newLines: list[int]
    charEncoding: tuple[Encoding, str]
    dataStream: Incomplete
    def __init__(self, source: _UnicodeInputStream) -> None: ...
    chunk: str
    chunkSize: int
    chunkOffset: int
    errors: list[str]
    prevNumLines: int
    prevNumCols: int
    def reset(self) -> None: ...

    @overload
    def openStream(self, source: _SupportsReadT) -> _SupportsReadT: ...
    @overload
    def openStream(self, source: str | None) -> StringIO: ...

    def position(self) -> tuple[int, int]: ...
    def char(self) -> str | None: ...
    def readChunk(self, chunkSize: int | None = None) -> bool: ...
    def characterErrorsUCS4(self, data: str) -> None: ...
    def characterErrorsUCS2(self, data: str) -> None: ...
    def charsUntil(self, characters: Iterable[str | bytes | bytearray], opposite: bool = False) -> str: ...
    def unget(self, char: str | None) -> None: ...

class HTMLBinaryInputStream(HTMLUnicodeInputStream):
    rawStream: Incomplete
    numBytesMeta: int
    numBytesChardet: int
    override_encoding: Incomplete
    transport_encoding: Incomplete
    same_origin_parent_encoding: Incomplete
    likely_encoding: Incomplete
    default_encoding: str
    charEncoding: tuple[Encoding, str]
    def __init__(
        self,
        source: _BinaryInputStream,
        override_encoding: str | bytes | None = None,
        transport_encoding: str | bytes | None = None,
        same_origin_parent_encoding: str | bytes | None = None,
        likely_encoding: str | bytes | None = None,
        default_encoding: str = "windows-1252",
        useChardet: bool = True,
    ) -> None: ...
    dataStream: Incomplete
    def reset(self) -> None: ...

    @overload  # type: ignore[override]
    def openStream(self, source: _SupportsReadBytesT) -> _SupportsReadBytesT: ...
    @overload  # type: ignore[override]
    def openStream(self, source: ReadableBuffer) -> BytesIO: ...

    def determineEncoding(self, chardet: bool = True): ...
    def changeEncoding(self, newEncoding: str | bytes | None) -> None: ...
    def detectBOM(self) -> Encoding | None: ...
    def detectEncodingMeta(self) -> Encoding | None: ...

class EncodingBytes(bytes):
    def __new__(self, value: bytes) -> Self: ...
    def __init__(self, value: bytes) -> None: ...
    def __iter__(self) -> Self: ...  # type: ignore[override]
    def __next__(self) -> bytes: ...
    def next(self) -> bytes: ...
    def previous(self) -> bytes: ...
    def setPosition(self, position: int) -> None: ...
    def getPosition(self) -> int | None: ...

    @property
    def position(self) -> int | None: ...
    @position.setter
    def position(self, position: int) -> None: ...

    def getCurrentByte(self) -> bytes: ...
    @property
    def currentByte(self) -> bytes: ...
    def skip(self, chars: bytes | bytearray | Iterable[bytes] = ...) -> bytes | None: ...
    def skipUntil(self, chars: bytes | bytearray | Iterable[bytes]) -> bytes | None: ...
    def matchBytes(self, bytes: bytes | bytearray) -> bool: ...
    def jumpTo(self, bytes: bytes | bytearray) -> Literal[True]: ...

class EncodingParser:
    data: EncodingBytes
    encoding: Encoding | None
    def __init__(self, data: bytes) -> None: ...
    def getEncoding(self) -> Encoding | None: ...
    def handleComment(self) -> bool: ...
    def handleMeta(self) -> bool: ...
    def handlePossibleStartTag(self) -> bool: ...
    def handlePossibleEndTag(self) -> bool: ...
    def handlePossibleTag(self, endTag: bool | None) -> bool: ...
    def handleOther(self) -> bool: ...
    def getAttribute(self) -> tuple[bytes, bytes] | None: ...

class ContentAttrParser:
    data: EncodingBytes
    def __init__(self, data: EncodingBytes) -> None: ...
    def parse(self) -> bytes | None: ...

def lookupEncoding(encoding: str | bytes | None) -> Encoding | None: ...
