from collections.abc import MutableMapping

from .storage import get_storage

_MISSING = object()


class pdict(MutableMapping):
    def __init__(self, name, initial=None):
        self._storage = get_storage()
        named_id = self._storage.get_named(name, "dict")
        if named_id is None:
            self.id = self._storage.generate_id()
            self._data = dict(initial) if initial is not None else {}
            self._persist()
            self._storage.set_named(name, self.id, "dict")
        else:
            self.id = named_id
            loaded = self._storage.get(named_id)
            if loaded is None:
                self._data = dict(initial) if initial is not None else {}
                self._persist()
            else:
                object_type, data = loaded
                if object_type != "dict":
                    raise TypeError("Stored object is not a dict.")
                self._data = dict(data)

    @classmethod
    def load(cls, name, initial=None):
        return cls(name=name, initial=initial)

    def __getitem__(self, key):
        return self._data[key]

    def __setitem__(self, key, value):
        self._data[key] = value
        self._persist()

    def __delitem__(self, key):
        del self._data[key]
        self._persist()

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)

    def __repr__(self):
        return repr(self._data)

    def __eq__(self, other):
        if isinstance(other, pdict):
            return self._data == other._data
        return self._data == other

    def clear(self):
        self._data.clear()
        self._persist()

    def pop(self, key, default=_MISSING):
        if default is _MISSING:
            value = self._data.pop(key)
        else:
            value = self._data.pop(key, default)
        self._persist()
        return value

    def popitem(self):
        item = self._data.popitem()
        self._persist()
        return item

    def setdefault(self, key, default=None):
        if key in self._data:
            return self._data[key]
        self._data[key] = default
        self._persist()
        return default

    def update(self, *args, **kwargs):
        self._data.update(*args, **kwargs)
        self._persist()

    def _persist(self):
        self._storage.save(self.id, "dict", self._data)
