from __future__ import annotations
import abc
from neo3 import storage
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from neo3.network import payloads


class Snapshot:
    def __init__(self):
        self._block_cache: storage.CachedBlockAccess = None
        self._contract_cache: storage.CachedContractAccess = None
        self._storage_cache: storage.CachedStorageAccess = None
        self._tx_cache: storage.CachedTXAccess = None
        self._block_height_cache: storage.AttributeCache = None
        self.persisting_block: payloads.Block = None

    @property
    def blocks(self):
        return self._block_cache

    @property
    def contracts(self):
        return self._contract_cache

    @property
    def storages(self):
        return self._storage_cache

    @property
    def transactions(self):
        return self._tx_cache

    @property
    def best_block_height(self) -> int:
        try:
            return self._block_height_cache.get()
        except KeyError:
            return -1

    @best_block_height.setter
    def best_block_height(self, value) -> None:
        self._block_height_cache.put(value)

    def commit(self):
        """
        Persist changes to the actual backend.
        """
        self._block_cache.commit()
        self._contract_cache.commit()
        self._storage_cache.commit()
        self._tx_cache.commit()
        self._block_height_cache.commit()

    def clone(self) -> CloneSnapshot:
        return CloneSnapshot(self)

    @abc.abstractmethod
    def __enter__(self):
        """"""

    @abc.abstractmethod
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Cleanup resources if necessary."""


class CloneSnapshot(Snapshot):
    def __init__(self, snapshot: Snapshot):
        super(CloneSnapshot, self).__init__()
        self._snapshot = snapshot
        self._block_cache = snapshot.blocks.create_snapshot()
        self._contract_cache = snapshot.contracts.create_snapshot()
        self._storage_cache = snapshot.storages.create_snapshot()
        self._tx_cache = snapshot.transactions.create_snapshot()
        self._block_height_cache = snapshot._block_height_cache.create_snapshot()
        self.persisting_block = snapshot.persisting_block

    def commit(self):
        """
        Persist changes to the parent snapshot.
        """
        super(CloneSnapshot, self).commit()

    def __enter__(self):
        yield self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
