from ..core import Event, EventData, Machine, State, Transition, CallbacksArg, Callback, ModelParameter, TransitionConfig
from collections import defaultdict as defaultdict
from typing import OrderedDict, Sequence, Union, List, Dict, Optional, Type, Tuple, Callable, Any, Collection
from types import TracebackType
from logging import Logger
from enum import Enum
from functools import partial

_LOGGER: Logger

class FunctionWrapper:
    _func: Optional[Callable]
    def __init__(self, func: Callable, path: List[str]) -> None: ...
    def add(self, func: Callable, path: List[str]) -> None: ...
    def __call__(self, *args: List, **kwargs: Dict[str, Any]) -> Any: ...


class NestedEvent(Event):
    def trigger_nested(self, event_data: NestedEventData) -> bool: ...
    def _process(self, event_data: NestedEventData) -> bool: ...  # type: ignore[override]


class NestedEventData(EventData):
    state: Optional[NestedState]
    event: Optional[NestedEvent]
    machine: Optional[HierarchicalMachine]
    transition: Optional[NestedTransition]
    source_name: Optional[str]
    source_path: Optional[List[str]]


class NestedState(State):
    separator: str
    initial: Optional[str]
    events: Dict[str, NestedEvent]
    states: OrderedDict[str, NestedState]
    _scope: List[str]
    def __init__(self, name: Union[str, Enum], on_enter: CallbacksArg = ..., on_exit: CallbacksArg = ...,
                 ignore_invalid_triggers: bool = ..., initial: Optional[str] = ...) -> None: ...
    def add_substate(self, state: NestedState) -> None: ...
    def add_substates(self, states: List[NestedState]) -> None: ...
    def scoped_enter(self, event_data: NestedEventData, scope: List[str]=...) -> None: ...
    def scoped_exit(self, event_data: NestedEventData, scope: List[str]=...) -> None: ...
    @property
    def name(self) -> str: ...

NestedStateIdentifier = Union[str, Enum, NestedState, Sequence[Union[str, Enum, NestedState, Sequence[Any]]]]
NestedStateConfig =  Union[NestedStateIdentifier, Dict[str, Any], Collection[str], 'HierarchicalMachine']
# mypy does not support cyclic definitions, use Any instead of `StateTree`
StateTree = OrderedDict[str, Any]

def _build_state_list(state_tree: StateTree, separator: str,
                      prefix: Optional[List[str]] = ...) -> Union[str, List[str]]: ...
def resolve_order(state_tree: Dict[str, str]) -> List[List[str]]: ...

class NestedTransition(Transition):
    def _resolve_transition(self, event_data: NestedEventData) -> Tuple[StateTree, List[partial], List[partial]]: ...
    def _change_state(self, event_data: NestedEventData) -> None: ...  # type: ignore[override]
    def _enter_nested(self, root: List[str], dest: List[str], prefix_path: List[str],
                      event_data: NestedEventData) -> Tuple[StateTree, List[partial]]: ...
    @staticmethod
    def _update_model(event_data: NestedEventData, tree: StateTree) -> None: ...
    def __deepcopy__(self, memo: Dict) -> NestedTransition: ...

ScopeTuple = Tuple[Union[NestedState, 'HierarchicalMachine'], OrderedDict[str, NestedState],
                   Dict[str, NestedEvent], List[str]]

class HierarchicalMachine(Machine):
    state_cls: Type[NestedState]
    transition_cls: Type[NestedTransition]
    event_cls: Type[NestedEvent]
    # mypy does not approve State being overridden with NestedState and Event with NestedEvent
    states: OrderedDict[str, NestedState]  # type: ignore
    events: Dict[str, NestedEvent]  # type:ignore
    _stack: List[ScopeTuple]
    _initial: Optional[str]
    prefix_path: List[str]
    scoped: Union[NestedState, HierarchicalMachine]
    def __init__(self, model: Optional[ModelParameter]=...,
                 states: Optional[Union[Sequence[NestedStateConfig], Type[Enum]]] = ...,
                 initial: Optional[NestedStateIdentifier] = ...,
                 transitions: Optional[Union[TransitionConfig, Sequence[TransitionConfig]]] = ...,
                 send_event: bool = ..., auto_transitions: bool = ..., ordered_transitions: bool = ...,
                 ignore_invalid_triggers: Optional[bool] = ...,
                 before_state_change: CallbacksArg = ..., after_state_change: CallbacksArg = ...,
                 name: str = ..., queued: Union[bool, str] = ...,
                 prepare_event: CallbacksArg = ..., finalize_event: CallbacksArg = ...,
                 model_attribute: str = ..., on_exception: CallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
    _next_scope: Optional[ScopeTuple]
    def __call__(self, to_scope: Optional[Union[ScopeTuple, str, Enum]] = ...) -> HierarchicalMachine: ...
    def __enter__(self) -> None: ...
    def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException],
                 exc_tb: Optional[TracebackType]) -> None: ...
    def add_model(self, model: ModelParameter, initial: Optional[NestedStateIdentifier] = ...) -> None: ...  # type: ignore[override]
    @property
    def initial(self) -> Optional[str]: ...
    @initial.setter
    def initial(self, value: NestedStateIdentifier) -> None: ...
    def add_ordered_transitions(self, states: Optional[Sequence[NestedState]] = ..., trigger: str = ..., loop: bool = ...,  # type: ignore[override]
                                loop_includes_initial: bool = ..., conditions: CallbacksArg = ...,
                                unless: CallbacksArg = ..., before: CallbacksArg = ..., after: CallbacksArg = ...,
                                prepare: CallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
    def add_states(self, states: Union[List[NestedStateConfig], NestedStateConfig],  # type: ignore[override]
                   on_enter: CallbacksArg = ...,  on_exit: CallbacksArg = ...,
                   ignore_invalid_triggers: Optional[bool] = ..., **kwargs: Dict[str, Any]) -> None: ...
    def add_transition(self, trigger: str,  # type: ignore[override]
                       source: Union[NestedStateIdentifier, List[NestedStateIdentifier]],
                       dest: Optional[NestedStateIdentifier] = ...,
                       conditions: CallbacksArg = ...,
                       unless: CallbacksArg = ..., before: CallbacksArg = ..., after: CallbacksArg = ...,
                       prepare: CallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
    def get_global_name(self, state: NestedStateIdentifier = ..., join: bool = ...) -> Union[str, List[str]]: ...
    def get_nested_state_names(self) -> List[str]: ...
    def get_nested_transitions(self, trigger: str = ..., src_path: Optional[List[str]] = ...,
                               dest_path: Optional[List[str]] = ...) -> List[NestedTransition]: ...
    def get_nested_triggers(self, src_path: Optional[List[str]] = ...) -> List[str]: ...
    def get_state(self, state: Union[str, Enum, List[str]], hint: Optional[List[str]] = ...) -> NestedState: ...
    def get_states(self, states: Union[str, Enum, List[Union[str, Enum]]]) -> List[NestedState]: ...
    def get_transitions(self, trigger: str = ..., source: NestedStateIdentifier = ...,  # type: ignore[override]
                        dest: NestedStateIdentifier = ..., delegate: bool = ...) -> List[NestedTransition]: ...
    def get_triggers(self, *args: Union[str, Enum, State]) -> List[str]: ...
    def has_trigger(self, trigger: str, state: Optional[NestedState] = ...) -> bool: ...
    def is_state(self, state: Union[str, Enum], model: object, allow_substates: bool = ...) -> bool: ...
    def on_enter(self, state_name: str, callback: Callback) -> None: ...
    def on_exit(self, state_name: str, callback: Callback) -> None: ...
    def set_state(self, state: Union[NestedStateIdentifier, List[NestedStateIdentifier]],  # type: ignore[override]
                  model: Optional[object] = ...) -> None: ...
    def to_state(self, model: object, state_name: str, *args: List, **kwargs: Dict[str, Any]) -> None: ...
    def trigger_event(self, model: object, trigger: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
    def _add_model_to_state(self, state: NestedState, model: object) -> None: ...  # type: ignore[override]
    def _add_dict_state(self, state: Dict[str, Any], ignore_invalid_triggers: bool, remap: Optional[Dict[str, str]],
                        **kwargs: Dict[str, Any]) -> None: ...
    def _add_enum_state(self, state: Enum, on_enter: CallbacksArg, on_exit: CallbacksArg, ignore_invalid_triggers: bool,
                        remap: Optional[Dict[str, str]], **kwargs: Dict[str, Any]) -> None: ...
    def _add_machine_states(self, state: HierarchicalMachine, remap: Optional[Dict[str, str]]) -> None: ...
    def _add_string_state(self, state: str, on_enter: CallbacksArg, on_exit: CallbacksArg,
                          ignore_invalid_triggers: bool, remap: Optional[Dict[str, str]],
                          **kwargs: Dict[str, Any]) -> None: ...
    def _add_trigger_to_model(self, trigger: str, model: object) -> None: ...
    def build_state_tree(self, model_states: Union[str, Enum, Sequence[Union[str, Enum, Sequence[Any]]]],
                         separator: str, tree: Optional[StateTree] = ...) -> StateTree: ...
    @classmethod
    def _create_transition(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedTransition: ...
    @classmethod
    def _create_event(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedEvent: ...
    @classmethod
    def _create_state(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedState: ...
    def _get_enum_path(self, enum_state: Enum, prefix: Optional[List[str]] =...) -> List[str]: ...
    def _get_state_path(self, state: NestedState, prefix: Optional[List[str]] = ...) -> List[str]: ...
    def _check_event_result(self, res: bool, model: object, trigger: str) -> bool: ...
    def _get_trigger(self, model: object, trigger_name: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
    def _has_state(self, state: NestedState, raise_error: bool = ...) -> bool: ...  # type: ignore[override]
    def _init_state(self, state: NestedState) -> None: ...
    def _recursive_initial(self, value: NestedStateIdentifier) -> Union[str, List[str]]: ...
    def _remap_state(self, state: NestedState, remap: Dict[str, str]) -> List[NestedTransition]: ...
    def _resolve_initial(self, models: List[object], state_name_path: List[str],
                         prefix: Optional[List[str]] = ...) -> str: ...
    def _set_state(self, state_name: Union[str, List[str]]) -> Union[str, Enum, List[Union[str, Enum]]]: ...
    def _trigger_event(self, event_data: NestedEventData, trigger: str) -> Optional[bool]: ...
