from __future__ import annotations

from typing import get_origin, Literal, get_args, Any, Mapping, Iterable
import copy
from abc import ABC

__all__ = ['Enum']


class _ImmutableWrapper(ABC):
    """Enum 속성의 내부 변경을 막기 위한 래퍼 기반 클래스."""

    __slots__ = ('_attr_name',)

    def __init__(self, attr_name: str) -> None:
        self._attr_name = attr_name

    @property
    def attr_name(self) -> str:
        """해당 래퍼가 보호하는 Enum 속성 이름."""
        return self._attr_name

    def _raise_error(self, *args: Any, **kwargs: Any) -> None:  # noqa: ARG002
        raise AttributeError(f"Enum의 속성 '{self._attr_name}'의 내부는 수정할 수 없습니다.")


class ImmutableDict(dict, _ImmutableWrapper):
    """내부 값 수정을 막고 커스텀 에러를 발생시키는 딕셔너리."""

    def __init__(self, attr_name: str, initial_data: Mapping | None = None) -> None:
        dict.__init__(self, {} if initial_data is None else initial_data)
        _ImmutableWrapper.__init__(self, attr_name)

    def __deepcopy__(self, memo: dict[int, Any]) -> dict:
        """deepcopy 시, 불변 래퍼를 벗기고 일반 dict로 복사합니다."""
        obj_id = id(self)
        if obj_id in memo:
            return memo[obj_id]

        new_dict: dict[Any, Any] = {}
        memo[obj_id] = new_dict

        for k, v in self.items():
            new_dict[copy.deepcopy(k, memo)] = copy.deepcopy(v, memo)

        return new_dict

    __setitem__ = _ImmutableWrapper._raise_error
    __delitem__ = _ImmutableWrapper._raise_error
    clear = _ImmutableWrapper._raise_error
    pop = _ImmutableWrapper._raise_error
    popitem = _ImmutableWrapper._raise_error
    setdefault = _ImmutableWrapper._raise_error
    update = _ImmutableWrapper._raise_error


class ImmutableList(list, _ImmutableWrapper):
    """내부 값 수정을 막고 커스텀 에러를 발생시키는 리스트."""

    def __init__(self, attr_name: str, initial_data: Iterable | None = None) -> None:
        list.__init__(self, [] if initial_data is None else initial_data)
        _ImmutableWrapper.__init__(self, attr_name)

    def __deepcopy__(self, memo: dict[int, Any]) -> list:
        """deepcopy 시, 불변 래퍼를 벗기고 일반 list로 복사합니다."""
        obj_id = id(self)
        if obj_id in memo:
            return memo[obj_id]

        new_list: list[Any] = []
        memo[obj_id] = new_list

        append = new_list.append
        for item in self:
            append(copy.deepcopy(item, memo))

        return new_list

    __setitem__ = _ImmutableWrapper._raise_error
    __delitem__ = _ImmutableWrapper._raise_error
    append = _ImmutableWrapper._raise_error
    clear = _ImmutableWrapper._raise_error
    extend = _ImmutableWrapper._raise_error
    insert = _ImmutableWrapper._raise_error
    pop = _ImmutableWrapper._raise_error
    remove = _ImmutableWrapper._raise_error
    reverse = _ImmutableWrapper._raise_error
    sort = _ImmutableWrapper._raise_error


# --- 컨테이너 불변 변환 함수 ---

def _make_immutable_custom(obj: Any, attr_name: str) -> Any:
    """dict / list (및 기타 컨테이너)를 불변 래퍼로 감쌉니다."""

    # 이미 우리의 래퍼라면 다시 감싸지 않습니다.
    if isinstance(obj, _ImmutableWrapper):
        return obj

    if isinstance(obj, dict):
        processed = {k: _make_immutable_custom(v, attr_name) for k, v in obj.items()}
        return ImmutableDict(attr_name, processed)

    if isinstance(obj, list):
        processed = [_make_immutable_custom(v, attr_name) for v in obj]
        return ImmutableList(attr_name, processed)

    # 필요 시 tuple / set 등도 내부만 재귀적으로 처리해서 재구성
    if isinstance(obj, tuple):
        return tuple(_make_immutable_custom(v, attr_name) for v in obj)

    if isinstance(obj, set):
        return frozenset(_make_immutable_custom(v, attr_name) for v in obj)

    return obj


# --- 메타클래스 ---

class NoReassign(type):
    """Enum 전용 메타클래스: 재할당 방지 및 컨테이너 불변 래핑."""

    def __new__(mcs, name: str, bases: tuple[type, ...], attrs: dict[str, Any]):
        immutable_attrs: dict[str, Any] = {}

        for k, v in attrs.items():
            if k.startswith('__') and k.endswith('__'):
                # 매직 메서드는 있는 그대로 둠
                immutable_attrs[k] = v
            else:
                immutable_attrs[k] = _make_immutable_custom(v, k)

        return super().__new__(mcs, name, bases, immutable_attrs)

    def __setattr__(cls, k: str, v: Any) -> None:  # noqa: D401
        """클래스 속성 재할당을 막습니다."""
        raise AttributeError(f"Enum의 속성 '{k}'는 재할당할 수 없습니다.")

    def __getattribute__(cls, item: str) -> Any:  # noqa: D401
        """Literal 속성은 값 리스트로 반환하고, 나머지는 그대로 반환합니다."""
        attr = object.__getattribute__(cls, item)
        if get_origin(attr) is Literal:
            return list(get_args(attr))
        return attr


class Enum(metaclass=NoReassign):
    """열거형 베이스 클래스: 컨테이너 불변 및 재할당 방지."""
    pass