from collections.abc import MutableSequence
from typing import Any, Optional


class Lict(MutableSequence):
    """ "列典" 对象

    同时兼容字典和列表大多数 API, 两边数据同步的容器, 性能与 list/dict 大体相当
    列表数据是 dictobj.items() 的格式
    支持根据字典或列表初始化
    限制要求:
    - 键名一定唯一, 且仅能为字符串
    - append 的元组中, 表示键名的元素不能重复, 否则会导致覆盖行为

    """

    def __init__(
        self,
        initlist: Optional[list] = None,
        initdict: Optional[dict] = None,
        forced_order: bool = False,
    ):
        self._dict = {}
        self._list = []
        self._dict_dirty = False
        self._list_dirty = False
        self.forced_order = forced_order

        if initdict:  # initdict 更优先
            self._dict = initdict.copy()
            self._list_dirty = True
        elif initlist:
            self._list = initlist.copy()
            self._dict_dirty = True

        self._sync_if_needed()

    def _sync_if_needed(self):
        """只在需要时同步"""
        if self._dict_dirty:
            self._dict = {k: v for k, v in self._list}
            if self.forced_order:
                self._list.sort()
            self._dict_dirty = False

        if self._list_dirty:
            self._list = list(self._dict.items())
            if self.forced_order:
                self._list.sort()
            self._list_dirty = False

    def __getitem__(self, key):
        if isinstance(key, str):
            return self._dict[key]
        else:
            self._sync_if_needed()
            return self._list[key]

    def __setitem__(self, key, value):
        """传入键值对时等同于操作字典, 传入索引+元组时等用于替换某索引的列表值为新元组"""
        if isinstance(key, str):
            self._dict[key] = value
            self._list_dirty = True
        else:
            if value != (value[0], value[1]):
                raise NotImplementedError
            self._sync_if_needed()
            old_key = self._list[key][0]
            self._dict.pop(old_key)
            self._list[key] = value
            self._dict[value[0]] = value[1]  # 避免全量同步

    def __delitem__(self, key):
        if isinstance(key, str):  # 字符串键
            del self._dict[key]
            self._list_dirty = True
        else:  # 数字索引
            self._sync_if_needed()
            del_key = self._list[key][0]
            del self._list[key]
            del self._dict[del_key]

    def keys(self):
        self._sync_if_needed()
        return self._dict.keys()

    def values(self):
        self._sync_if_needed()
        return self._dict.values()

    def items(self):
        self._sync_if_needed()
        return self._list

    def __len__(self):
        self._sync_if_needed()
        return len(self._list)

    def __iter__(self):
        self._sync_if_needed()
        return iter(self._list)

    def __contains__(self, item):
        self._sync_if_needed()
        return item in self._list or item in self.keys() or item in self.values()

    def append(self, item):
        if item != (item[0], item[1]):
            raise NotImplementedError
        self._sync_if_needed()  # 以防 forced_order
        key, value = item
        self._dict[key] = value
        self._list.append(item)  # 两端都已同步
        self._sync_if_needed()  # 以防 forced_order

    def append_if_it_doesnt_exist_before(self, item: Any):
        if item != (item[0], item[1]):
            raise NotImplementedError
        self._sync_if_needed()
        if item[0] not in self:
            self.append(item)
        self._sync_if_needed()

    def insert(self, i: int, item: Any) -> None:
        if item != (item[0], item[1]):
            raise NotImplementedError
        self._sync_if_needed()
        self._list.insert(i, item)
        self._dict_dirty = True

    def pop(self, i: int = -1) -> Any:
        self._sync_if_needed()
        res = self._list.pop(i)
        self._dict_dirty = True
        return res

    def remove(self, item: Any) -> None:
        if isinstance(item, str):
            item = (item, self._dict[item])
        if item != (item[0], item[1]):
            raise NotImplementedError
        self._sync_if_needed()
        self._list.remove(item)
        self._dict_dirty = True

    def clear(self) -> None:
        self._dict.clear()
        self._list.clear()
        self._dict_dirty = False
        self._list_dirty = False

    def index(self):
        raise NotImplementedError

    def extend(self):
        raise NotImplementedError

    def sort(self):
        raise NotImplementedError

    def reverse(self):
        raise NotImplementedError

    def get_itemic_unit(self, ident):
        return (ident, self._dict[ident])

    def keys_equal_with(self, other):
        self._sync_if_needed()
        return self.key_equality(self, other)

    @classmethod
    def key_equality(cls, a, b):
        return a.keys() == b.keys()
