#
# Copyright (c) Daniel Sheffield 2023
# All rights reserved
#
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
from typing import Dict
from .CachedLoadingPage import CachedLoadingPage

def key_to_hash(key):
    if isinstance(key, tuple):
        orig, _hash = key
    else:
        if isinstance(key, int):
            orig, _hash = None, key
        else:
            orig, _hash = key, None

    if None not in (orig, _hash):
        if hash(orig) != _hash:
            raise KeyError(f"Invalid key: {key}")

        return _hash

    if (_hash, orig) is (None, None):
        raise KeyError(f"Invalid key: {key}")

    return hash(orig) if _hash is None else _hash

class Cache:
    def __init__(self, limit) -> None:
        self._cache: Dict[str, CachedLoadingPage] = dict()
        self._limit = limit

    def __delitem__(self, key):
        self._cache.pop(key_to_hash(key), None)

    def __getitem__(self, key):
        return self.get(key_to_hash(key))

    def __setitem__(self, key, value):
        self._cache[key_to_hash(key)] = value

    def get(self, key: str) -> str:
        key = key_to_hash(key)
        if key not in self._cache:
            return None
        
        page = self._cache[key]
        if page.stale:
            del self._cache[key]
            return None
        return page.value if page.loaded else page.update()
    
    def _enforce_limit(self, limit):
        for idx, (_, k) in enumerate(sorted([
                (v.age, k) for k, v in self._cache.items()
            ])):
            if idx >= limit: del self._cache[k]

    def _clear_stale(self):
        for k in [k for k, v in self._cache.items() if v.stale]:
            del self._cache[k]
    
    def add(self, key: str, page: CachedLoadingPage) -> str:
        self._clear_stale()
        self._enforce_limit(self._limit)
        self._cache[key_to_hash(key)] = page
        return page.value
    
    def remove(self, key: str):
        key = key_to_hash(key)
        if key in self._cache:
            del self._cache[key]

