import time
import typing as tp
from satella.coding.structures import CacheDict
from .. import Metric
from ..metric_types import ClicksPerTimeUnitMetric


class MetrifiedCacheDict(CacheDict):
    """
    A CacheDict with metrics!

    :param cache_hits: a counter metric that will be updated with +1 each time there's a cache hit
    :param cache_miss: a counter metric that will be updated with +1 each time there's a cache miss
    :param refreshes: a metric that will be updated with +1 each time there's a cache refresh
    :param how_long_refresh_takes: a metric that will be ticked with time value_getter took
    """
    def __init__(self, stale_interval, expiration_interval, value_getter,
                 value_getter_executor=None, cache_failures_interval=None,
                 time_getter=time.monotonic,
                 default_value_factory=None,
                 cache_hits: tp.Optional[Metric] = None,
                 cache_miss: tp.Optional[Metric] = None,
                 refreshes: tp.Optional[Metric] = None,
                 how_long_refresh_takes: tp.Optional[Metric] = None):
        super().__init__(stale_interval, expiration_interval, value_getter,
                         value_getter_executor, cache_failures_interval, time_getter,
                         default_value_factory)
        self.cache_hits = cache_hits
        self.cache_miss = cache_miss
        self.refreshes = refreshes
        self.how_long_refresh_takes = how_long_refresh_takes

    def __getitem__(self, item):
        if self.has_info_about(item):
            if self.cache_hits:
                self.cache_hits.runtime(+1)
        else:
            if self.cache_miss:
                self.cache_miss.runtime(+1)
        return super().__getitem__(item)

    def schedule_a_fetch(self, key):
        if self.refreshes:
            self.refreshes.runtime(+1)
        time_start = self.time_getter()
        fut = super().schedule_a_fetch(key)

        def on_done_callback(future):
            elapsed = self.time_getter() - time_start
            self.how_long_refresh_takes.runtime(elapsed)

        if self.how_long_refresh_takes:
            fut.add_done_callback(on_done_callback)
        return fut

@@ -73,16 +73,49 @@
 cheDict%0A
+from satella.time import measure%0A
 from .. 
@@ -128,16 +128,16 @@
  Metric%0A
-
 from ..m
@@ -1125,24 +1125,550 @@
 c%5D = None):%0A
+        if refreshes or how_long_refresh_takes:%0A            def value_getter_replacement():%0A                with measure() as measurement:%0A                    try:%0A                        return value_getter()%0A                    finally:%0A                        if self.refreshes:%0A                            self.refreshes.runtime(+1)%0A                        if self.how_long_refresh_takes:%0A                            self.how_long_refresh_takes.runtime(measurement())%0A%0A            value_getter = value_getter_replacement%0A%0A
         supe
@@ -2265,32 +2265,32 @@
 iss.runtime(+1)%0A
+
         return s
@@ -2318,454 +2318,4 @@
 em)%0A
-%0A    def schedule_a_fetch(self, key):%0A        if self.refreshes:%0A            self.refreshes.runtime(+1)%0A        time_start = self.time_getter()%0A        fut = super().schedule_a_fetch(key)%0A%0A        def on_done_callback(future):%0A            elapsed = self.time_getter() - time_start%0A            self.how_long_refresh_takes.runtime(elapsed)%0A%0A        if self.how_long_refresh_takes:%0A            fut.add_done_callback(on_done_callback)%0A        return fut%0A

refactor metrics in cache dict