import sys
from _typeshed import StrOrBytesPath
from collections.abc import Callable, Hashable, Iterable, Sequence
from configparser import RawConfigParser
from re import Pattern
from threading import Thread
from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload
from typing_extensions import Required, TypeAlias

from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level

DEFAULT_LOGGING_CONFIG_PORT: int
RESET_ERROR: Final[int]  # undocumented
IDENTIFIER: Final[Pattern[str]]  # undocumented

if sys.version_info >= (3, 11):
    class _RootLoggerConfiguration(TypedDict, total=False):
        level: _Level
        filters: Sequence[str | _FilterType]
        handlers: Sequence[str]

else:
    class _RootLoggerConfiguration(TypedDict, total=False):
        level: _Level
        filters: Sequence[str]
        handlers: Sequence[str]

class _LoggerConfiguration(_RootLoggerConfiguration, TypedDict, total=False):
    propagate: bool

_FormatterConfigurationTypedDict = TypedDict(
    "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False
)

class _FilterConfigurationTypedDict(TypedDict):
    name: str

# Formatter and filter configs can specify custom factories via the special `()` key.
# If that is the case, the dictionary can contain any additional keys
# https://docs.python.org/3/library/logging.config.html#user-defined-objects
_FormatterConfiguration: TypeAlias = _FormatterConfigurationTypedDict | dict[str, Any]
_FilterConfiguration: TypeAlias = _FilterConfigurationTypedDict | dict[str, Any]
# Handler config can have additional keys even when not providing a custom factory so we just use `dict`.
_HandlerConfiguration: TypeAlias = dict[str, Any]

class _DictConfigArgs(TypedDict, total=False):
    version: Required[Literal[1]]
    formatters: dict[str, _FormatterConfiguration]
    filters: dict[str, _FilterConfiguration]
    handlers: dict[str, _HandlerConfiguration]
    loggers: dict[str, _LoggerConfiguration]
    root: _RootLoggerConfiguration
    incremental: bool
    disable_existing_loggers: bool

# Accept dict[str, Any] to avoid false positives if called with a dict
# type, since dict types are not compatible with TypedDicts.
#
# Also accept a TypedDict type, to allow callers to use TypedDict
# types, and for somewhat stricter type checking of dict literals.
def dictConfig(config: _DictConfigArgs | dict[str, Any]) -> None: ...

if sys.version_info >= (3, 10):
    def fileConfig(
        fname: StrOrBytesPath | IO[str] | RawConfigParser,
        defaults: dict[str, str] | None = None,
        disable_existing_loggers: bool = True,
        encoding: str | None = None,
    ) -> None: ...

else:
    def fileConfig(
        fname: StrOrBytesPath | IO[str] | RawConfigParser,
        defaults: dict[str, str] | None = None,
        disable_existing_loggers: bool = True,
    ) -> None: ...

def valid_ident(s: str) -> Literal[True]: ...  # undocumented
def listen(port: int = 9030, verify: Callable[[bytes], bytes | None] | None = None) -> Thread: ...
def stopListening() -> None: ...

class ConvertingMixin:  # undocumented
    def convert_with_key(self, key: Any, value: Any, replace: bool = True) -> Any: ...
    def convert(self, value: Any) -> Any: ...

class ConvertingDict(dict[Hashable, Any], ConvertingMixin):  # undocumented
    def __getitem__(self, key: Hashable) -> Any: ...
    def get(self, key: Hashable, default: Any = None) -> Any: ...
    def pop(self, key: Hashable, default: Any = None) -> Any: ...

class ConvertingList(list[Any], ConvertingMixin):  # undocumented
    @overload
    def __getitem__(self, key: SupportsIndex) -> Any: ...
    @overload
    def __getitem__(self, key: slice) -> Any: ...
    def pop(self, idx: SupportsIndex = -1) -> Any: ...

class ConvertingTuple(tuple[Any, ...], ConvertingMixin):  # undocumented
    @overload
    def __getitem__(self, key: SupportsIndex) -> Any: ...
    @overload
    def __getitem__(self, key: slice) -> Any: ...

class BaseConfigurator:  # undocumented
    CONVERT_PATTERN: Pattern[str]
    WORD_PATTERN: Pattern[str]
    DOT_PATTERN: Pattern[str]
    INDEX_PATTERN: Pattern[str]
    DIGIT_PATTERN: Pattern[str]
    value_converters: dict[str, str]
    importer: Callable[..., Any]

    def __init__(self, config: _DictConfigArgs | dict[str, Any]) -> None: ...
    def resolve(self, s: str) -> Any: ...
    def ext_convert(self, value: str) -> Any: ...
    def cfg_convert(self, value: str) -> Any: ...
    def convert(self, value: Any) -> Any: ...
    def configure_custom(self, config: dict[str, Any]) -> Any: ...
    def as_tuple(self, value: list[Any] | tuple[Any]) -> tuple[Any]: ...

class DictConfigurator(BaseConfigurator):
    def configure(self) -> None: ...  # undocumented
    def configure_formatter(self, config: _FormatterConfiguration) -> Formatter | Any: ...  # undocumented
    def configure_filter(self, config: _FilterConfiguration) -> Filter | Any: ...  # undocumented
    def add_filters(self, filterer: Filterer, filters: Iterable[_FilterType]) -> None: ...  # undocumented
    def configure_handler(self, config: _HandlerConfiguration) -> Handler | Any: ...  # undocumented
    def add_handlers(self, logger: Logger, handlers: Iterable[str]) -> None: ...  # undocumented
    def common_logger_config(
        self, logger: Logger, config: _LoggerConfiguration, incremental: bool = False
    ) -> None: ...  # undocumented
    def configure_logger(self, name: str, config: _LoggerConfiguration, incremental: bool = False) -> None: ...  # undocumented
    def configure_root(self, config: _LoggerConfiguration, incremental: bool = False) -> None: ...  # undocumented

dictConfigClass = DictConfigurator
