# == cache.py ==#

from __future__ import annotations

from collections import deque
from typing import Any, Deque, Tuple, TypeVar, Dict

__all__: Tuple[str, ...] = ('ObjectCache',)


K = TypeVar('K')
V = TypeVar('V')


class _BaseCache(Dict[K, V]):
    """This is a rough implementation of an LRU Cache using a deque and a dict."""

    __slots__: Tuple[str, ...] = ('_max_size', '_lru_keys')

    def __init__(self, max_size: int, *args: Any) -> None:
        self._max_size: int = max(min(max_size, 30), 0)  # bounding max_size to 15 for now
        self._lru_keys: Deque[K] = deque(maxlen=self._max_size)
        super().__init__(*args)

    def __getitem__(self, __k: K) -> V:
        index = self._lru_keys.index(__k)
        target = self._lru_keys[index]
        del self._lru_keys[index]

        self._lru_keys.appendleft(target)
        return super().__getitem__(__k)

    def __setitem__(self, __k: K, __v: V) -> None:
        if len(self) == self._max_size:
            self.__delitem__(self._lru_keys.pop())

        self._lru_keys.appendleft(__k)
        return super().__setitem__(__k, __v)

    def update(self, **kwargs: Any) -> None:
        for key, value in dict(**kwargs).items():
            key: K
            value: V

            self.__setitem__(key, value)


class ObjectCache(_BaseCache[K, V]):
    """This adjusts the typehints to reflect Github objects."""

    def __getitem__(self, __k: K) -> V:
        index = self._lru_keys.index(__k)
        target = self._lru_keys[index]
        self._lru_keys.appendleft(target)
        return super().__getitem__(__k)

    def __setitem__(self, __k: K, __v: V) -> None:
        if self.__len__() == self._max_size:
            self.__delitem__(self._lru_keys.pop())

        self._lru_keys.appendleft(__k)
        return super().__setitem__(__k, __v)

    def update(self, **kwargs: Any) -> None:
        for key, value in dict(**kwargs).items():
            key: K
            value: V

            self.__setitem__(key, value)
