from multiprocessing import current_process
from threading import Thread
from time import perf_counter, sleep
from abc import ABC, abstractmethod


class Cacher(ABC):
    """
    DEPRECTED! Use redis
    Implements an autorenewable cache for some data
    """

    name = "Abstract cache"
    renew_interval = None
    timeout = 60
    _data = None

    def __init__(self):
        Thread(target=cache_renewer_worker, daemon=True, args=[self]).start()

    def get_data(self):
        """
        Returnes data from cache.
        """
        start = perf_counter()
        while self._data is None:
            if perf_counter() - start > self.timeout:
                raise Exception(f'Data acqisition timeout for cache "{self.name}"')
            sleep(0.2)
        return self._data

    def is_ready(self):
        """Returns True if data acquired and ready to use"""
        return self._data is not None

    @abstractmethod
    def get_data_func(self):
        """Must return something that will be stored into _data"""
        pass


def cache_renewer_worker(cacher: Cacher):
    cacher._data = cacher.get_data_func()
    if cacher.renew_interval:
        while True:
            sleep(cacher.renew_interval)
            cacher._data = cacher.get_data_func()


CACHES_BY_PID = {}  # process id -> cache name -> Cacher

REGISTERED_CACHERS = {}  # cache name -> (Cacher class, autoload)


def register_cacher(cacher_class, autoload: bool = False):
    """DEPRECTED! Use redis"""
    name = cacher_class.name
    REGISTERED_CACHERS[name] = (cacher_class, autoload)
    if autoload:
        get_cache(name)


def get_cache(name) -> Cacher:
    """DEPRECTED! Use redis"""
    pid = current_process().pid
    if pid not in CACHES_BY_PID:
        names2init = set(k for k, v in REGISTERED_CACHERS.items() if v[1])
        names2init.add(name)
        CACHES_BY_PID[pid] = {n: REGISTERED_CACHERS[n][0]() for n in names2init}
    elif name not in CACHES_BY_PID[pid]:
        CACHES_BY_PID[pid][name] = REGISTERED_CACHERS[name][0]()

    return CACHES_BY_PID[pid][name]
