from collections.abc import Callable, Hashable
from datetime import date
from re import Pattern
from typing import Any, ClassVar, TypeVar

from yaml.error import MarkedYAMLError
from yaml.nodes import MappingNode, Node, ScalarNode, SequenceNode

from .cyaml import _CLoader
from .loader import _Loader

_L = TypeVar("_L", bound=_Loader | _CLoader)
_N = TypeVar("_N", bound=Node)

class ConstructorError(MarkedYAMLError): ...

class BaseConstructor:
    yaml_constructors: Any
    yaml_multi_constructors: Any
    constructed_objects: Any
    recursive_objects: Any
    state_generators: Any
    deep_construct: Any
    def __init__(self) -> None: ...
    def check_data(self): ...
    def check_state_key(self, key: str) -> None: ...
    def get_data(self): ...
    def get_single_data(self) -> Any: ...
    def construct_document(self, node): ...
    def construct_object(self, node, deep=False): ...
    def construct_scalar(self, node: ScalarNode) -> str: ...
    def construct_sequence(self, node: SequenceNode, deep: bool = False) -> list[Any]: ...
    def construct_mapping(self, node: MappingNode, deep: bool = False) -> dict[Hashable, Any]: ...
    def construct_pairs(self, node, deep=False): ...
    @classmethod
    # Use typevars so we can have covariant behaviour in the parameter types
    def add_constructor(cls, tag: str, constructor: Callable[[_L, _N], Any]) -> None: ...
    @classmethod
    def add_multi_constructor(cls, tag_prefix, multi_constructor): ...

class SafeConstructor(BaseConstructor):
    def construct_scalar(self, node: ScalarNode | MappingNode) -> str: ...
    def flatten_mapping(self, node: MappingNode) -> None: ...
    def construct_mapping(self, node: MappingNode, deep: bool = False) -> dict[Hashable, Any]: ...
    def construct_yaml_null(self, node: ScalarNode) -> None: ...
    bool_values: ClassVar[dict[str, bool]]
    def construct_yaml_bool(self, node: ScalarNode) -> bool: ...
    def construct_yaml_int(self, node: ScalarNode) -> int: ...
    inf_value: ClassVar[float]
    nan_value: ClassVar[float]
    def construct_yaml_float(self, node: ScalarNode) -> float: ...
    def construct_yaml_binary(self, node: ScalarNode) -> bytes: ...
    timestamp_regexp: ClassVar[Pattern[str]]
    def construct_yaml_timestamp(self, node: ScalarNode) -> date: ...
    def construct_yaml_omap(self, node): ...
    def construct_yaml_pairs(self, node): ...
    def construct_yaml_set(self, node): ...
    def construct_yaml_str(self, node): ...
    def construct_yaml_seq(self, node): ...
    def construct_yaml_map(self, node): ...
    def construct_yaml_object(self, node, cls): ...
    def construct_undefined(self, node): ...

class FullConstructor(SafeConstructor):
    def get_state_keys_blacklist(self) -> list[str]: ...
    def get_state_keys_blacklist_regexp(self) -> Pattern[str]: ...
    def construct_python_str(self, node): ...
    def construct_python_unicode(self, node): ...
    def construct_python_bytes(self, node): ...
    def construct_python_long(self, node): ...
    def construct_python_complex(self, node): ...
    def construct_python_tuple(self, node): ...
    def find_python_module(self, name: str, mark, unsafe: bool = False): ...
    def find_python_name(self, name: str, mark, unsafe: bool = False): ...
    def construct_python_name(self, suffix, node): ...
    def construct_python_module(self, suffix, node): ...
    def make_python_instance(self, suffix, node, args=None, kwds=None, newobj: bool = False, unsafe: bool = False): ...
    def set_python_instance_state(self, instance: Any, state, unsafe: bool = False) -> None: ...
    def construct_python_object(self, suffix, node): ...
    def construct_python_object_apply(self, suffix, node, newobj=False): ...
    def construct_python_object_new(self, suffix, node): ...

class UnsafeConstructor(FullConstructor):
    def find_python_module(self, name: str, mark): ...  # type: ignore[override]
    def find_python_name(self, name: str, mark): ...  # type: ignore[override]
    def make_python_instance(self, suffix: str, node, args=None, kwds=None, newobj: bool = False): ...  # type: ignore[override]
    def set_python_instance_state(self, instance: Any, state): ...  # type: ignore[override]

class Constructor(SafeConstructor):
    def construct_python_str(self, node): ...
    def construct_python_unicode(self, node): ...
    def construct_python_long(self, node): ...
    def construct_python_complex(self, node): ...
    def construct_python_tuple(self, node): ...
    def find_python_module(self, name, mark): ...
    def find_python_name(self, name, mark): ...
    def construct_python_name(self, suffix, node): ...
    def construct_python_module(self, suffix, node): ...
    def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): ...
    def set_python_instance_state(self, instance, state): ...
    def construct_python_object(self, suffix, node): ...
    def construct_python_object_apply(self, suffix, node, newobj=False): ...
    def construct_python_object_new(self, suffix, node): ...
