"""Error-mapping middleware.

This middleware is the place where we translate *backend exceptions* into
*domain exceptions* using an error-handler chain.

Ordering note:
- We typically apply policy middleware (like retries) before error mapping so policy
  can see raw backend failures.
- We apply instrumentation (logging/metrics) outside so it measures what callers see.
"""

from __future__ import annotations

from typing import Any, Callable, Generic, TypeVar

from ..execution import BaseExecRequest, Executor
from .base import _call_maybe_async

TReq = TypeVar("TReq", bound=BaseExecRequest)
TOut = TypeVar("TOut")


class ErrorMappingMiddleware(Generic[TReq, TOut]):
    """Map backend exceptions to domain exceptions using an error handler chain."""

    def __init__(self, *, error_handlers: Any, ctx_factory: Callable[[TReq], Any]) -> None:
        self._errors = error_handlers
        self._ctx_factory = ctx_factory

    def wrap(self, nxt: Executor[TReq, TOut]) -> Executor[TReq, TOut]:
        errors = self._errors
        ctx_factory = self._ctx_factory

        class _Wrapped:
            def execute(self, req: TReq) -> TOut:
                return _call_maybe_async(
                    lambda: nxt.execute(req),
                    on_error=lambda e: errors.handle(e, ctx_factory(req)),
                )

        return _Wrapped()

