import sys
from abc import ABCMeta, abstractmethod
from collections.abc import Iterator
from io import BufferedReader
from typing import IO, Any, Literal, Protocol, overload, runtime_checkable

if sys.version_info >= (3, 11):
    class ResourceReader(metaclass=ABCMeta):
        @abstractmethod
        def open_resource(self, resource: str) -> IO[bytes]: ...
        @abstractmethod
        def resource_path(self, resource: str) -> str: ...
        if sys.version_info >= (3, 10):
            @abstractmethod
            def is_resource(self, path: str) -> bool: ...
        else:
            @abstractmethod
            def is_resource(self, name: str) -> bool: ...

        @abstractmethod
        def contents(self) -> Iterator[str]: ...

    @runtime_checkable
    class Traversable(Protocol):
        @abstractmethod
        def is_dir(self) -> bool: ...
        @abstractmethod
        def is_file(self) -> bool: ...
        @abstractmethod
        def iterdir(self) -> Iterator[Traversable]: ...
        if sys.version_info >= (3, 11):
            @abstractmethod
            def joinpath(self, *descendants: str) -> Traversable: ...
        else:
            @abstractmethod
            def joinpath(self, child: str, /) -> Traversable: ...

        # The documentation and runtime protocol allows *args, **kwargs arguments,
        # but this would mean that all implementers would have to support them,
        # which is not the case.
        @overload
        @abstractmethod
        def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ...
        @overload
        @abstractmethod
        def open(self, mode: Literal["rb"]) -> IO[bytes]: ...
        @property
        @abstractmethod
        def name(self) -> str: ...
        if sys.version_info >= (3, 10):
            def __truediv__(self, child: str, /) -> Traversable: ...
        else:
            @abstractmethod
            def __truediv__(self, child: str, /) -> Traversable: ...

        @abstractmethod
        def read_bytes(self) -> bytes: ...
        @abstractmethod
        def read_text(self, encoding: str | None = None) -> str: ...

    class TraversableResources(ResourceReader):
        @abstractmethod
        def files(self) -> Traversable: ...
        def open_resource(self, resource: str) -> BufferedReader: ...
        def resource_path(self, resource: Any) -> str: ...
        def is_resource(self, path: str) -> bool: ...
        def contents(self) -> Iterator[str]: ...

    __all__ = ["ResourceReader", "Traversable", "TraversableResources"]
