"""Global terminal error registry for restate_saga."""

from __future__ import annotations

from typing import TYPE_CHECKING

import restate

if TYPE_CHECKING:
    from restate_saga.types import ErrorMapper

# ---------------------------------------------------------------------------
# Global state
# ---------------------------------------------------------------------------

_terminal_error_classes: set[type[Exception]] = set()
_global_mapper: ErrorMapper | None = None

# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def register_terminal_errors(error_classes: list[type[Exception]]) -> None:
    """Register error classes that should always be treated as TerminalError."""
    for cls in error_classes:
        _terminal_error_classes.add(cls)


def unregister_terminal_errors(error_classes: list[type[Exception]]) -> None:
    """Unregister error classes from the terminal error registry."""
    for cls in error_classes:
        _terminal_error_classes.discard(cls)


def clear_terminal_errors() -> None:
    """Clear all registered terminal error classes."""
    _terminal_error_classes.clear()


def set_global_error_mapper(mapper: ErrorMapper | None) -> None:
    """Set a global error mapper function (or None to clear)."""
    global _global_mapper  # noqa: PLW0603
    _global_mapper = mapper


def get_global_error_mapper() -> ErrorMapper | None:
    """Get the current global error mapper function."""
    return _global_mapper


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------


def _check_global_terminal_error(
    err: Exception,
) -> restate.TerminalError | None:
    """Check if an error should be terminal based on global configuration."""
    for error_class in _terminal_error_classes:
        if isinstance(err, error_class):
            return restate.TerminalError(str(err))

    if _global_mapper:
        return _global_mapper(err)

    return None


def resolve_terminal_error(
    err: Exception,
    step_mapper: ErrorMapper | None = None,
) -> restate.TerminalError | None:
    """Resolve whether an error should be terminal.

    Resolution order:
    1. Step-level ``as_terminal_error`` mapper (if provided)
    2. Global registered error classes
    3. Global error mapper function
    4. ``None`` — error will be retried by Restate
    """
    if step_mapper:
        result = step_mapper(err)
        if result:
            return result

    return _check_global_terminal_error(err)
