from typing import Iterator, Literal, overload

from .._types import _FileReadSource
from . import LxmlError, _ElementOrAnyTree, _Validator

class DTDError(LxmlError): ...
class DTDParseError(DTDError): ...
class DTDValidateError(DTDError): ...

class _DTDElementContentDecl:
    @property
    def name(self) -> str | None: ...
    @property
    def type(self) -> Literal["pcdata", "element", "seq", "or"] | None: ...
    @property
    def occur(self) -> Literal["once", "opt", "mul", "plus"] | None: ...
    @property
    def left(self) -> _DTDElementContentDecl | None: ...
    @property
    def right(self) -> _DTDElementContentDecl | None: ...

class _DTDAttributeDecl:
    @property
    def name(self) -> str | None: ...
    @property
    def elemname(self) -> str | None: ...
    @property
    def prefix(self) -> str | None: ...
    @property
    def type(
        self,
    ) -> Literal[
        "cdata",
        "id",
        "idref",
        "idrefs",
        "entity",
        "entities",
        "nmtoken",
        "nmtokens",
        "enumeration",
        "notation",
    ] | None: ...
    @property
    def default(self) -> Literal["none", "required", "implied", "fixed"] | None: ...
    @property
    def default_value(self) -> str | None: ...
    def itervalues(self) -> Iterator[str]: ...
    def values(self) -> list[str]: ...

class _DTDElementDecl:
    @property
    def name(self) -> str | None: ...
    @property
    def prefix(self) -> str | None: ...
    @property
    def type(
        self,
    ) -> Literal["undefined", "empty", "any", "mixed", "element"] | None: ...
    @property
    def content(self) -> _DTDElementContentDecl | None: ...
    def iterattributes(self) -> Iterator[_DTDAttributeDecl]: ...
    def attributes(self) -> list[_DTDAttributeDecl]: ...

class _DTDEntityDecl:
    @property
    def name(self) -> str | None: ...
    @property
    def orig(self) -> str | None: ...
    @property
    def content(self) -> str | None: ...
    @property
    def system_url(self) -> str | None: ...

class DTD(_Validator):
    # external_id is effective only when file is None, and
    # native string raises exception
    @overload
    def __init__(self, file: _FileReadSource) -> None: ...
    @overload
    def __init__(self, file: None = ..., *, external_id: bytes) -> None: ...
    @property
    def name(self) -> str | None: ...
    @property
    def external_id(self) -> str | None: ...
    @property
    def system_url(self) -> str | None: ...
    def iterelements(self) -> Iterator[_DTDElementDecl]: ...
    def elements(self) -> list[_DTDElementDecl]: ...
    def iterentities(self) -> Iterator[_DTDEntityDecl]: ...
    def entities(self) -> list[_DTDEntityDecl]: ...
    def __call__(self, etree: _ElementOrAnyTree) -> bool: ...
