import time
from functools import partialmethod

from clarabot_nano.utility.aio_sqlite import AbstractAioSQLiteDatabase


class IndexCacheDatabase(AbstractAioSQLiteDatabase):
    """
    Some data to be queried from the Nano changes seldom and is costly to prepare. An example would be some aggregate
    data from the index by a directory. Such values can be cached and invalidated by the indexer when changes occur.
    """

    def _initialise(self):
        """
        This database holds small aggregate data for keys and their creation timestamp for invalidation later.
        Writes need not be highly durable since all data is only cached here, meaning they can be re-generated at will.
        The WAL mode will provide high write throughput and non-blocking reads in particular, for optimal performance.
        """
        self._con.execute("PRAGMA journal_mode = WAL;")
        self._con.execute("PRAGMA synchronous = NORMAL;")

    def _setup(self):
        self._con.execute(
            "CREATE TABLE IF NOT EXISTS CachedData0 ("
            " key BLOB INTEGER PRIMARY KEY,"
            " value BLOB,"
            " validity BLOB,"
            " cache_time INTEGER"
            ")"
        )
        self._con.execute("CREATE INDEX IF NOT EXISTS CachedData0__cache_time ON CachedData0(cache_time)")

    def _get(self, key):
        """
        Get data for use along with its validity.
        """
        rows = self._con.execute("SELECT value, validity FROM CachedData0 WHERE key = ?", (key,)).fetchall()
        if rows:
            return rows[0]

    def _set(self, key, value, validity):
        """
        Store the data in the cache.
        """
        self._con.execute(
            "REPLACE INTO CachedData0(key, value, validity, cache_time) VALUES (?, ?, ?, ?)",
            (key, value, validity, int(time.time())),
        )

    def _drop_old(self, cache_time):
        """
        Drop items that are older than the given time.
        """
        self._con.execute("DELETE FROM CachedData0 WHERE cache_time <= ?", (cache_time,))

    get = partialmethod(AbstractAioSQLiteDatabase._run_job, _get, begin_immediate=False)
    set = partialmethod(AbstractAioSQLiteDatabase._run_job, _set)
    drop_old = partialmethod(AbstractAioSQLiteDatabase._run_job, _drop_old)
