"""Python wrapper for Expat parser."""

import sys
from _typeshed import ReadableBuffer, SupportsRead
from collections.abc import Callable
from pyexpat import errors as errors, model as model
from typing import Any, Final, TypeAlias, final
from typing_extensions import CapsuleType
from xml.parsers.expat import ExpatError as ExpatError

EXPAT_VERSION: Final[str]  # undocumented
version_info: tuple[int, int, int]  # undocumented
native_encoding: str  # undocumented
features: list[tuple[str, int]]  # undocumented

error = ExpatError
XML_PARAM_ENTITY_PARSING_NEVER: Final = 0
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: Final = 1
XML_PARAM_ENTITY_PARSING_ALWAYS: Final = 2

_Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]]

@final
class XMLParserType:
    """XML parser"""

    def Parse(self, data: str | ReadableBuffer, isfinal: bool = False, /) -> int:
        """Parse XML data.

        'isfinal' should be true at end of input.
        """

    def ParseFile(self, file: SupportsRead[bytes], /) -> int:
        """Parse XML data from file-like object."""

    def SetBase(self, base: str, /) -> None:
        """Set the base URL for the parser."""

    def GetBase(self) -> str | None:
        """Return base URL string for the parser."""

    def GetInputContext(self) -> bytes | None:
        """Return the untranslated text of the input that caused the current event.

        If the event was generated by a large amount of text (such as
        a start tag for an element with many attributes), not all of the
        text may be available.
        """

    def ExternalEntityParserCreate(self, context: str | None, encoding: str = ..., /) -> XMLParserType:
        """Create a parser for parsing an external entity based on the information passed to the ExternalEntityRefHandler."""

    def SetParamEntityParsing(self, flag: int, /) -> int:
        """Controls parsing of parameter entities (including the external DTD subset).

        Possible flag values are XML_PARAM_ENTITY_PARSING_NEVER,
        XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE and
        XML_PARAM_ENTITY_PARSING_ALWAYS. Returns true if setting the flag
        was successful.
        """

    def UseForeignDTD(self, flag: bool = True, /) -> None:
        """Allows the application to provide an artificial external subset if one is not specified as part of the document instance.

        This readily allows the use of a 'default' document type controlled
        by the application, while still getting the advantage of providing
        document type information to the parser.  'flag' defaults to True if
        not provided.
        """

    def GetReparseDeferralEnabled(self) -> bool:
        """Retrieve reparse deferral enabled status; always returns false with Expat <2.6.0."""

    def SetReparseDeferralEnabled(self, enabled: bool, /) -> None:
        """Enable/Disable reparse deferral; enabled by default with Expat >=2.6.0."""

    # Added in Python 3.10.20, 3.11.15, 3.12.3, 3.13.10, 3.14.1
    def SetAllocTrackerActivationThreshold(self, threshold: int, /) -> None:
        """Sets the number of allocated bytes of dynamic memory needed to activate protection against disproportionate use of RAM.

        Parser objects usually have an allocation activation threshold of
        64 MiB, but the actual default value depends on the underlying Expat
        library.
        """

    def SetAllocTrackerMaximumAmplification(self, max_factor: float, /) -> None:
        """Sets the maximum amplification factor between direct input and bytes of dynamic memory allocated.

        The amplification factor is calculated as "allocated / direct" while
        parsing, where "direct" is the number of bytes read from the primary
        document in parsing and "allocated" is the number of bytes of
        dynamic memory allocated in the parser hierarchy.

        The 'max_factor' value must be a non-NaN floating point value
        greater than or equal to 1.0.  Amplification factors greater than
        100.0 can be observed near the start of parsing even with benign
        files in practice.  In particular, the activation threshold should
        be carefully chosen to avoid false positives.

        Parser objects usually have a maximum amplification factor of 100,
        but the actual default value depends on the underlying Expat
        library.
        """

    if sys.version_info >= (3, 13):
        # Added in Python 3.13.4, 3.14.6
        def SetBillionLaughsAttackProtectionActivationThreshold(self, threshold: int, /) -> None:
            """Sets the number of output bytes needed to activate protection against billion laughs attacks.

            The number of output bytes includes amplification from entity
            expansion and reading DTD files.

            Parser objects usually have a protection activation threshold of
            8 MiB, but the actual default value depends on the underlying Expat
            library.

            Activation thresholds below 4 MiB are known to break support for
            DITA 1.3 payload and are hence not recommended.
            """

        def SetBillionLaughsAttackProtectionMaximumAmplification(self, max_factor: float, /) -> None:
            """Sets the maximum tolerated amplification factor for protection against billion laughs attacks.

            The amplification factor is calculated as "(direct + indirect) /
            direct" while parsing, where "direct" is the number of bytes read
            from the primary document in parsing and "indirect" is the number of
            bytes added by expanding entities and reading external DTD files,
            combined.

            The 'max_factor' value must be a non-NaN floating point value
            greater than or equal to 1.0.  Amplification factors greater than
            30,000 can be observed in the middle of parsing even with benign
            files in practice.  In particular, the activation threshold should
            be carefully chosen to avoid false positives.

            Parser objects usually have a maximum amplification factor of 100,
            but the actual default value depends on the underlying Expat
            library.
            """

    @property
    def intern(self) -> dict[str, str]: ...
    buffer_size: int
    buffer_text: bool
    buffer_used: int
    namespace_prefixes: bool  # undocumented
    ordered_attributes: bool
    specified_attributes: bool
    ErrorByteIndex: int
    ErrorCode: int
    ErrorColumnNumber: int
    ErrorLineNumber: int
    CurrentByteIndex: int
    CurrentColumnNumber: int
    CurrentLineNumber: int
    XmlDeclHandler: Callable[[str, str | None, int], Any] | None
    StartDoctypeDeclHandler: Callable[[str, str | None, str | None, bool], Any] | None
    EndDoctypeDeclHandler: Callable[[], Any] | None
    ElementDeclHandler: Callable[[str, _Model], Any] | None
    AttlistDeclHandler: Callable[[str, str, str, str | None, bool], Any] | None
    StartElementHandler: (
        Callable[[str, dict[str, str]], Any]
        | Callable[[str, list[str]], Any]
        | Callable[[str, dict[str, str], list[str]], Any]
        | None
    )
    EndElementHandler: Callable[[str], Any] | None
    ProcessingInstructionHandler: Callable[[str, str], Any] | None
    CharacterDataHandler: Callable[[str], Any] | None
    UnparsedEntityDeclHandler: Callable[[str, str | None, str, str | None, str], Any] | None
    EntityDeclHandler: Callable[[str, bool, str | None, str | None, str, str | None, str | None], Any] | None
    NotationDeclHandler: Callable[[str, str | None, str, str | None], Any] | None
    StartNamespaceDeclHandler: Callable[[str, str], Any] | None
    EndNamespaceDeclHandler: Callable[[str], Any] | None
    CommentHandler: Callable[[str], Any] | None
    StartCdataSectionHandler: Callable[[], Any] | None
    EndCdataSectionHandler: Callable[[], Any] | None
    DefaultHandler: Callable[[str], Any] | None
    DefaultHandlerExpand: Callable[[str], Any] | None
    NotStandaloneHandler: Callable[[], int] | None
    ExternalEntityRefHandler: Callable[[str, str | None, str | None, str | None], int] | None
    SkippedEntityHandler: Callable[[str, bool], Any] | None

def ErrorString(code: int, /) -> str:
    """Returns string error for given number."""

# intern is undocumented
def ParserCreate(
    encoding: str | None = None, namespace_separator: str | None = None, intern: dict[str, Any] | None = None
) -> XMLParserType:
    """Return a new XML parser object."""

expat_CAPI: CapsuleType
