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

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

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

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

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

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

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

@type_check_only
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]

@type_check_only
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: Mapping[str, str] | None = None,
        disable_existing_loggers: bool = True,
        encoding: str | None = None,
    ) -> None: ...

else:
    def fileConfig(
        fname: StrOrBytesPath | IO[str] | RawConfigParser,
        defaults: Mapping[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[SupportsIndex | None]) -> Any: ...
    def pop(self, idx: SupportsIndex = -1) -> Any: ...

if sys.version_info >= (3, 12):
    class ConvertingTuple(tuple[Any, ...], ConvertingMixin):  # undocumented
        @overload
        def __getitem__(self, key: SupportsIndex) -> Any: ...
        @overload
        def __getitem__(self, key: slice[SupportsIndex | None]) -> Any: ...

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

class BaseConfigurator:
    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]

    config: dict[str, Any]  # undocumented

    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
