"""Unified facade for rendering state machines in multiple text formats.

The :class:`Formatter` class provides a decorator-based registry where each
renderer declares the format names it handles.  Adding a new format only
requires writing a renderer function and decorating it — no changes to
``__format__``, ``factory.py``, or ``statemachine.py``.

A module-level :data:`formatter` instance is the single public entry point::

    from statemachine.contrib.diagram import formatter

    print(formatter.render(sm, "mermaid"))

    @formatter.register_format("plantuml")
    def _render_plantuml(machine):
        ...
"""

from typing import TYPE_CHECKING
from typing import Callable
from typing import Dict
from typing import List

if TYPE_CHECKING:
    from typing import Union

    from statemachine.statemachine import StateChart

    MachineRef = Union["StateChart", "type[StateChart]"]


class Formatter:
    """Unified facade for rendering state machines in multiple text formats."""

    def __init__(self) -> None:
        self._formats: Dict[str, "Callable[[MachineRef], str]"] = {}

    def register_format(
        self, *names: str
    ) -> "Callable[[Callable[[MachineRef], str]], Callable[[MachineRef], str]]":
        """Decorator factory that registers a renderer under one or more format names.

        Usage::

            @formatter.register_format("md", "markdown")
            def _render_md(machine_or_class):
                ...
        """

        def decorator(
            fn: "Callable[[MachineRef], str]",
        ) -> "Callable[[MachineRef], str]":
            for name in names:
                self._formats[name] = fn
            return fn

        return decorator

    def render(self, machine_or_class: "MachineRef", fmt: str) -> str:
        """Render a state machine in the given text format.
3:import pickle
17:def copy_pickle(obj):
18:    return pickle.loads(pickle.dumps(obj))
21:@pytest.fixture(params=[deepcopy, copy_pickle], ids=["deepcopy", "pickle"])
86:def test_copy(copy_method):
99:def test_copy_with_listeners(copy_method):
126:def test_copy_with_enum(copy_method):
135:def test_copy_with_custom_init_and_vars(copy_method):
156:def test_copy_async_statemachine_before_activation(copy_method):
157:    """Regression test for issue #544: async SM fails after pickle/deepcopy.
