# -*- coding: utf-8 -*-
"""Generic TMDb / ČSFD response cache on top of the single metadata cache (`meta` in library.db).

Replaces the old tmdb_cache.json (docs/plan/milionar-library/030 §3): same call signatures, but every entry is a
`meta` row with the key `http:<sha256 of endpoint|params>`. The lifetime is chosen by the reader (`expire_seconds`),
rows are kept for 30 days and purged on the first write of a process. Series, season, movie and collection data do
not go through here; they use `wcs.library2.tmdb` and their canonical keys.
"""
import hashlib
import json
import time

import xbmc

from wcs.library2 import meta

PREFIX = 'http:'
KEEP_SECONDS = 30 * 24 * 3600
_purged = False


def _make_key(endpoint, params):
    """Stable key of an endpoint and its parameters."""
    raw_key = '{}|{}'.format(endpoint, json.dumps(params, sort_keys=True, ensure_ascii=False))
    return PREFIX + hashlib.sha256(raw_key.encode('utf-8')).hexdigest()


def get_from_cache(endpoint, params, expire_seconds=86400):
    """Cached data or None when missing / older than `expire_seconds`."""
    try:
        entry = meta.get_aged(_make_key(endpoint, params))
    except Exception as exc:
        xbmc.log('[Milionář Cache] read failed: {}: {}'.format(type(exc).__name__, exc), xbmc.LOGWARNING)
        return None
    if not entry or entry[1] > expire_seconds:
        return None
    return entry[0]


def set_to_cache(endpoint, params, data):
    global _purged
    try:
        if not _purged:
            _purged = True
            old = [k for k in meta.stale_keys(time.time()) if k.startswith(PREFIX)]
            meta.delete_keys(old)
        meta.put(_make_key(endpoint, params), data, KEEP_SECONDS)
    except Exception as exc:
        xbmc.log('[Milionář Cache] write failed: {}: {}'.format(type(exc).__name__, exc), xbmc.LOGWARNING)


def get_or_fetch(endpoint, params, fetch_fn, expire_seconds=86400):
    """Cached data, otherwise `fetch_fn()` (no arguments); a non-None result is stored."""
    data = get_from_cache(endpoint, params, expire_seconds)
    if data is not None:
        return data
    data = fetch_fn()
    if data is not None:
        set_to_cache(endpoint, params, data)
    return data


def delete(endpoint, params):
    """Drop one entry; returns 1 when it existed."""
    key = _make_key(endpoint, params)
    if meta.get_aged(key) is None:
        return 0
    return meta.delete_keys([key])


def delete_where(predicate):
    """Drop every generic entry whose data satisfies `predicate(data)`; returns the count."""
    return meta.delete_keys(k for k, data in meta.items_with_prefix(PREFIX) if predicate(data))
