from __future__ import annotations

import inspect
import operator
import sys
import types
import typing
from functools import reduce, wraps


def _resolve_annotation(annotation: object, dataclass_type: type[object] | None) -> object:
    if isinstance(annotation, property):
        getter = getattr(annotation, "fget", None)
        target_name = getattr(getter, "__name__", None)
        module = inspect.getmodule(getter) or inspect.getmodule(dataclass_type)
        target = getattr(module, target_name, None) if module is not None and target_name else None
        return target if isinstance(target, type) else annotation

    args = typing.get_args(annotation)
    if not args:
        return annotation

    resolved_args = tuple(_resolve_annotation(arg, dataclass_type) for arg in args)
    if resolved_args == args:
        return annotation

    origin = typing.get_origin(annotation)
    if origin in (typing.Union, types.UnionType) or str(origin) == "typing.Union":
        return reduce(operator.or_, resolved_args)

    try:
        if len(resolved_args) == 1:
            return origin[resolved_args[0]]
        return origin[resolved_args]
    except Exception:
        return annotation


def apply_asyncua_python314_compat() -> None:
    if sys.version_info < (3, 14):
        return

    from asyncua.ua import ua_binary

    if getattr(ua_binary, "_cf_python314_annotation_patch", False):
        return

    original_field_serializer = ua_binary.field_serializer
    original_create_type_serializer = ua_binary.create_type_serializer
    original_create_type_deserializer = ua_binary._create_type_deserializer

    @wraps(original_field_serializer)
    def patched_field_serializer(field_type, dataclass_type):
        return original_field_serializer(_resolve_annotation(field_type, dataclass_type), dataclass_type)

    @wraps(original_create_type_serializer)
    def patched_create_type_serializer(ua_type):
        return original_create_type_serializer(_resolve_annotation(ua_type, None))

    @wraps(original_create_type_deserializer)
    def patched_create_type_deserializer(ua_type, dataclass_type):
        return original_create_type_deserializer(_resolve_annotation(ua_type, dataclass_type), dataclass_type)

    if hasattr(original_create_type_serializer, "cache_clear"):
        original_create_type_serializer.cache_clear()
    if hasattr(original_create_type_deserializer, "cache_clear"):
        original_create_type_deserializer.cache_clear()
    if hasattr(ua_binary.create_dataclass_serializer, "cache_clear"):
        ua_binary.create_dataclass_serializer.cache_clear()
    if hasattr(ua_binary._create_dataclass_deserializer, "cache_clear"):
        ua_binary._create_dataclass_deserializer.cache_clear()

    ua_binary.field_serializer = patched_field_serializer
    ua_binary.create_type_serializer = patched_create_type_serializer
    ua_binary._create_type_deserializer = patched_create_type_deserializer
    ua_binary._cf_python314_annotation_patch = True