"""Tests for the error registry."""

import restate

from restate_saga.error_registry import (
    clear_terminal_errors,
    get_global_error_mapper,
    register_terminal_errors,
    resolve_terminal_error,
    set_global_error_mapper,
    unregister_terminal_errors,
)


class ValidationError(Exception):
    pass


class NotFoundError(Exception):
    pass


class InternalError(Exception):
    pass


def setup_function():
    """Clean state before each test."""
    clear_terminal_errors()
    set_global_error_mapper(None)


# ---- register / unregister / clear ----


def test_register_and_resolve():
    register_terminal_errors([ValidationError])
    result = resolve_terminal_error(ValidationError("bad input"))
    assert isinstance(result, restate.TerminalError)
    assert "bad input" in str(result)


def test_unregistered_error_returns_none():
    result = resolve_terminal_error(InternalError("oops"))
    assert result is None


def test_unregister():
    register_terminal_errors([ValidationError])
    unregister_terminal_errors([ValidationError])
    assert resolve_terminal_error(ValidationError("bad")) is None


def test_clear():
    register_terminal_errors([ValidationError, NotFoundError])
    clear_terminal_errors()
    assert resolve_terminal_error(ValidationError("x")) is None
    assert resolve_terminal_error(NotFoundError("y")) is None


# ---- global mapper ----


def test_global_mapper():
    def mapper(err):
        if isinstance(err, InternalError):
            return restate.TerminalError("mapped")
        return None

    set_global_error_mapper(mapper)
    assert get_global_error_mapper() is mapper

    result = resolve_terminal_error(InternalError("fail"))
    assert isinstance(result, restate.TerminalError)

    # Non-matching error
    assert resolve_terminal_error(ValueError("other")) is None


def test_clear_global_mapper():
    set_global_error_mapper(lambda e: restate.TerminalError("x"))
    set_global_error_mapper(None)
    assert get_global_error_mapper() is None
    assert resolve_terminal_error(Exception("any")) is None


# ---- resolution order ----


def test_step_mapper_takes_precedence():
    register_terminal_errors([ValidationError])

    def step_mapper(err):
        if isinstance(err, ValidationError):
            return restate.TerminalError("from step")
        return None

    result = resolve_terminal_error(ValidationError("x"), step_mapper)
    assert isinstance(result, restate.TerminalError)
    assert "from step" in str(result)


def test_falls_through_to_global_when_step_mapper_returns_none():
    register_terminal_errors([NotFoundError])

    def step_mapper(err):
        return None  # doesn't handle anything

    result = resolve_terminal_error(NotFoundError("missing"), step_mapper)
    assert isinstance(result, restate.TerminalError)
    assert "missing" in str(result)


def test_registered_class_checked_before_global_mapper():
    register_terminal_errors([ValidationError])

    calls = []

    def global_mapper(err):
        calls.append(err)
        return restate.TerminalError("from global")

    set_global_error_mapper(global_mapper)

    result = resolve_terminal_error(ValidationError("x"))
    assert isinstance(result, restate.TerminalError)
    # Global mapper should NOT have been called because registered class matched first
    assert len(calls) == 0
