import sys
import typing
from types import NoneType

__all__ = (
    "ExceptionGroup",
    "evaluate_forwardref",
)


if sys.version_info < (3, 11):
    from exceptiongroup import ExceptionGroup as ExceptionGroup
else:
    ExceptionGroup = ExceptionGroup


def evaluate_forwardref(
    value: typing.Any,
    globalns: dict[str, typing.Any] | None = None,
    localns: dict[str, typing.Any] | None = None,
    type_params: tuple[typing.Any, ...] | None = None,
) -> typing.Any:
    """Behaves like typing._eval_type, except it won't raise an error if a forward reference can't be resolved."""
    if value is None:
        value = NoneType

    elif isinstance(value, str):
        value = typing.ForwardRef(value, is_argument=False, is_class=True)

    try:
        return eval_type_backport(value, globalns, localns, type_params=type_params)
    except NameError:
        # the point of this function is to be tolerant to this case
        return value


def eval_type_backport(
    value: typing.Any,
    globalns: dict[str, typing.Any] | None = None,
    localns: dict[str, typing.Any] | None = None,
    type_params: tuple[typing.Any, ...] | None = None,
) -> typing.Any:
    """Like `typing._eval_type`, but falls back to the `eval_type_backport` package if it's
    installed to let older Python versions use newer typing features.
    Specifically, this transforms `X | Y` into `typing.Union[X, Y]`
    and `list[X]` into `typing.List[X]` etc. (for all the types made generic in PEP 585)
    if the original syntax is not supported in the current Python version.
    """
    try:
        if sys.version_info >= (3, 13):
            return typing._eval_type(  # type: ignore
                value, globalns, localns, type_params=type_params
            )

        else:
            return typing._eval_type(  # type: ignore
                value, globalns, localns
            )

    except TypeError as e:
        if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)):
            raise

        try:
            from eval_type_backport import eval_type_backport as _eval_type_backport

        except ImportError:
            raise TypeError(
                f"You have a type annotation {value.__forward_arg__!r} "
                f"which makes use of newer typing features than are supported in your version of Python. "
                f"To handle this error, you should either remove the use of new syntax "
                f"or install the `eval_type_backport` package."
            ) from e

        else:
            return _eval_type_backport(value, globalns, localns, try_default=False)


def is_backport_fixable_error(e: TypeError) -> bool:
    msg = str(e)
    return (
        msg.startswith("unsupported operand type(s) for |: ")
        or "' object is not subscriptable" in msg
    )
